In this note, we generate the input files for GSEA to conduct gene set enrichment analysis of the identified genes differentially expressed between given groups of patients.

TPM adjusted data

We generate input files for the differential expressed genes between nonacute stage I untreated vs all other groups based on Laura’s observations.

varnames<-c("nodule_lymph_pheno","steroid_atv1","dmard_atv1")
my_gsea_filecreate_binary(fpkm.filepath=fpkm.file,
                          clinic.filepath = clinic.file,
                          var.names = varnames,
                          group1.values = data.frame(nodule_lymph_pheno=c("lymph"),steroid_atv1=c(" 0"),dmard_atv1=c(" 0")),
                          group2.values = data.frame(nodule_lymph_pheno=c("micronodule","both"),steroid_atv1=c(" 0"," 0"),dmard_atv1=c(" 0"," 0")),
                          output.dir=output.folder,
                          suffix.name="analysis4"
                          )
There are  43  and  75  samples for group1 and group2, respectively.
[1] 0

DESeq2 log2 adjusted data

GSEA input files generation

We first generate file containing both the clinical matrix and CT scan features so that both group 1 and group 2 in the GSEA analysis can be defined based on combinations of clinical triats and CT scan data.

# These codes only need to be run once
# load in the clinical matrix
clinic.filepath<-"/home/yanxiting/driver_Grace/scratch/GRADS/SARC_results/Results_summary_PBMC_hg38/baseline/data/clinic_matrix_merged.RDS"
clinic.matrix<-readRDS(clinic.filepath,refhook = NULL)
# load in the CT scan data
ct.filepath<-file.path(home.dir,"scratch/GRADS/SARC_results/Data/CT_data_20170712/Sarc_ct_reads_corrected.xls")
ct.matrix<-read.xls(ct.filepath,sheet=1)
rownames(ct.matrix)<-as.matrix(ct.matrix)[,"GRADSID"]
ct.matrix<-ct.matrix[,c("Med_Lymphadenopathy","Hilar_Lymphadenopathy","Micronodule","Bronchial_Wall_Thickening","Traction_Bronchiectasis","Bronchiectasis_severity","Ground_Glass","Honeycombing","Reticular_Abnormality","Pulmonary_Art","Tree_in_bud")]

# load in the fpkm matrix
fpkm.file=file.path(home.dir,"scratch/GRADS/SARC_results/Results_summary_PBMC_hg38/baseline/data_adjusted2","DESeq2_normalized_276_clean_log2_celldiffadjusted_withannot.txt")

fpkm.matrix<-read.table(fpkm.file,sep="\t",header=T,check.names=F,as.is=TRUE,comment.char = "")

fpkm.matrix.anno<-fpkm.matrix[,1:6]
fpkm.matrix<-fpkm.matrix[,7:ncol(fpkm.matrix)]

# merge the two data
clinic.matrix<-clinic.matrix[colnames(fpkm.matrix),]
ct.matrix<-ct.matrix[colnames(fpkm.matrix),]
rownames(clinic.matrix)<-colnames(fpkm.matrix)
rownames(ct.matrix)<-colnames(fpkm.matrix)

cmd.out<-cbind(clinic.matrix,ct.matrix)
output.filepath<-file.path(home.dir,"scratch/GRADS/SARC_results/Results_summary_PBMC_hg38/baseline/data/clinic_ct_merge_matrix_withgex.RDS")
saveRDS(cmd.out,file=output.filepath,refhook = NULL)

output.filepath<-file.path(home.dir,"scratch/GRADS/SARC_results/Results_summary_PBMC_hg38/baseline/data/clinic_ct_merge_matrix_withgex.txt")
write.table(cmd.out,file=output.filepath,sep="\t",row.names=F,col.names=T,quote=F,append=F)

# change the file access right so that these two files won't be changed accidentally
output.filepath<-file.path(home.dir,"scratch/GRADS/SARC_results/Results_summary_PBMC_hg38/baseline/data/clinic_ct_merge_matrix_withgex.RDS")
system(paste("chmod a-w ",output.filepath,sep=""))

output.filepath<-file.path(home.dir,"scratch/GRADS/SARC_results/Results_summary_PBMC_hg38/baseline/data/clinic_ct_merge_matrix_withgex.txt")
system(paste("chmod a-w ",output.filepath,sep=""))

Also generate the clinical matrix file.

# These codes only need to be run once.
clinic.filepath<-"/home/yanxiting/driver_Grace/scratch/GRADS/SARC_results/Results_summary_PBMC_hg38/baseline/data/clinic_matrix_merged.RDS"
clinic.matrix<-readRDS(clinic.filepath,refhook = NULL)
output.filepath<-"/home/yanxiting/driver_Grace/scratch/GRADS/SARC_results/Results_summary_PBMC_hg38/baseline/data/clinic_matrix_merged.txt"
write.table(clinic.matrix,file=output.filepath,sep="\t",row.names=F,col.names=T,quote=F,append=F)

We generate input files for the differential expressed genes between nonacute stage I untreated vs all other groups based on Laura’s observations.


# output the merged clinical data matrix as a txt file
clinic.filepath<-"/home/yanxiting/driver_Grace/scratch/GRADS/SARC_results/Results_summary_PBMC_hg38/baseline/data/clinic_matrix_merged.RDS"
clinic.matrix<-readRDS(clinic.filepath,refhook = NULL)
#output.filepath<-"/home/yanxiting/driver_Grace/scratch/GRADS/SARC_results/Results_summary_PBMC_hg38/baseline/data/clinic_matrix_merged.txt"
#write.table(clinic.matrix,file=output.filepath,sep="\t",row.names=F,col.names=T,quote=F,append=F)


# note this function is slightly different for DESeq2 Norm data and the TPM matrix
my_gsea_filecreate_binary<-function(fpkm.filepath,clinic.filepath,var.names,group1.values,group2.values,group1.name="group1",group2.name="group2",output.dir,suffix.name){
  #####################################################################################################################################################
  # Arguments:
  # 
  # gexp.filepath is the file path of the TPM matrix with first 5 columns being annotation for genes 
  # (TPM_baseline_276_clean_celldiffadjusted_withannot.txt).
  # 
  # clinic.filpath is the file path of the clinic data (clinic_matrix_merged.txt). The first row needs to be the column names.
  # 
  # var.names is a vector containing the names of columns in clinic.filepath that you want to generate the phenotype file for.
  #
  # group1.values is a data.frame of string describing values of var.names that are considered as gorup 1. 
  # For example, group1.values=data.frame(PHENGRP="nonacute Stage I untreated"). Note that the columns in group1.values need to match those in var.names if there are
  # more than 1 variable to define the groups.
  #
  # group2.values has the same format as group1.values but is the settings for group2.
  #
  # group1.name is the name you want to call your group1 in the cls file.
  #
  # group2.name is the name you want to call your group2 in the cls file.
  # 
  # output.dir is the folder name where you want to save the created files.
  #
  # suffix.name is the suffix name in the name of the output files. The gct file will be named as gct_suffix.name.gct and cls file will be named as
  # cls_suffix.name.gct. For example, if suffix.name="test", the gct file will be named "gct_test.gct" and the cls file will be named as 
  # "cls_test.cls".
  # 
  # Value:
  #
  # This function will return 0 if successfully ran. Otherwise, it will return 1. If unsuccessful, information regarding what went wrong will be spit 
  # out as warning messages.
  #
  # When ran successfully, this function will create two files under the specified output.dir named gct_var.name.gct and cls_var.name.cls, where 
  # var.name will be the speficied value 
  # in the argument. These two files can be directly loaded into GSEA together with another gene set file that needs to be generated outside of this 
  # function.
  #####################################################################################################################################################
  
  #  generate the gene expression matrix input file for GSEA
  #fpkm.filepath<-file.path(home.dir,"scratch/GRADS/SARC_results/Results_summary_PBMC_hg38/baseline/data_adjusted2","TPM_baseline_276_clean_celldiffadjusted_withannot.txt")
  #clinic.filepath<-"/home/yanxiting/driver_Grace/scratch/GRADS/SARC_results/Results_summary_PBMC_hg38/baseline/data/clinic_matrix_merged.txt"
  #output.dir<-"/home/yanxiting/driver_Grace/scratch/GRADS/SARC_results/Results_summary_PBMC_hg38/baseline/GSEA_knowngenes"
  # suffix.name<-"analysis1"
  
  
  # load in the fpkm matrix
  fpkm.matrix<-read.table(fpkm.filepath,sep="\t",header=T,check.names=F,as.is=TRUE,comment.char = "")
  fpkm.matrix.anno<-fpkm.matrix[,1:6]
  fpkm.matrix<-fpkm.matrix[,7:ncol(fpkm.matrix)]
  
  # load in the clinical data and subset the samples to those listed in the fpkm.matrix
  clinic.matrix=read.table(clinic.filepath,sep="\t",header=T,check.names=F,stringsAsFactors=F)
  rownames(clinic.matrix)<-as.matrix(clinic.matrix)[,1] # GRADS ID
  clinic.matrix<-clinic.matrix[colnames(fpkm.matrix),]

  # based on var.name, group1.values, and group2.values, identify the samples to include in the files.
  if(file.exists(output.dir)==F){
    dir.create(output.dir)
  }
  
  gct.filepath<-file.path(output.dir,paste("gexp_",suffix.name,".gct",sep=""))
  cls.filepath<-file.path(output.dir,paste("cls_",suffix.name,".cls",sep=""))
  
  # substract samples with var.name equal to the values in group1.values and group2. values
  temp1<-fpkm.matrix[,apply(as.data.frame(clinic.matrix[,var.names]),1,paste,collapse="_")%in%apply(group1.values,1,paste,collapse="_")]
  #cat("\n")
  #cat(apply(as.data.frame(clinic.matrix[,var.names]),1,paste,collapse="_"))
  #cat("\n")
  temp2<-fpkm.matrix[,apply(as.data.frame(clinic.matrix[,var.names]),1,paste,collapse="_")%in%apply(group2.values,1,paste,collapse="_")]
  cat("There are ",ncol(temp1)," and ", ncol(temp2)," samples for group1 and group2, respectively.\n")
  data.matrix<-cbind(temp1,temp2)
  data.matrix<-cbind(fpkm.matrix.anno[,1],rep("na",nrow(data.matrix)),data.matrix)
  colnames(data.matrix)[1:2]<-c("NAME","Description")
  pheno.vect<-c(rep(0,ncol(temp1)),rep(1,ncol(temp2)))
  
  # output the gene expression data
  cmd.out<-"#1.2\n"
  cmd.out<-paste(cmd.out,nrow(data.matrix),"\t",ncol(data.matrix)-2,"\n",sep="")
  cat(cmd.out,file=gct.filepath,append=F)
  write.table(data.matrix,sep="\t",file=gct.filepath,append=T,row.names=F,col.names=T,quote=F)
  
  # output the phenotype file
  cmd.out<-paste(ncol(data.matrix)-2," ",2," ",1,"\n",sep="")
  cmd.out<-paste(cmd.out,"# group1 group2\n",sep="")
  cat(cmd.out,file=cls.filepath,append=F,sep="")
  cat(pheno.vect,file=cls.filepath,append=T,sep=" ")
  
  results<-list(group1=temp1,group2=temp2)
  
  return(results)
}



fpkm.file=file.path(home.dir,"scratch/GRADS/SARC_results/Results_summary_PBMC_hg38/baseline/data_adjusted2","DESeq2_normalized_276_clean_log2_celldiffadjusted_withannot.txt")
clinic.file<-"/home/yanxiting/driver_Grace/scratch/GRADS/SARC_results/Results_summary_PBMC_hg38/baseline/data/clinic_matrix_merged.txt"
output.folder<-"/home/yanxiting/driver_Grace/scratch/GRADS/SARC_results/Results_summary_PBMC_hg38/baseline/GSEA_knowngenes_DESeq2_log2adjusted"

# generate the files for analysis 1
varnames<-c("PHENGRP")
temp<-my_gsea_filecreate_binary(fpkm.filepath=fpkm.file,
                          clinic.filepath = clinic.file,
                          var.names = varnames,
                          group1.values = data.frame(PHENGRP=c("Non-acute, Stage I, untreated")),
                          group2.values = data.frame(PHENGRP=c("Stage II-III, untreated","Stage IV, untreated")),
                          output.dir=output.folder,
                          suffix.name="analysis1"
                          )
There are  29  and  68  samples for group1 and group2, respectively.
# generate the files for analysis 2
varnames<-c("PHENGRP")
temp<-my_gsea_filecreate_binary(fpkm.filepath=fpkm.file,
                          clinic.filepath = clinic.file,
                          var.names = varnames,
                          group1.values = data.frame(PHENGRP=c("Remitting, untreated")),
                          group2.values = data.frame(PHENGRP=c("Non-acute, Stage I, untreated","Stage II-III, untreated","Stage IV, untreated")),
                          output.dir=output.folder,
                          suffix.name="analysis2"
                          )
There are  40  and  97  samples for group1 and group2, respectively.
# generate the files for analysis 3
varnames<-c("PHENGRP")
temp<-my_gsea_filecreate_binary(fpkm.filepath=fpkm.file,
                          clinic.filepath = clinic.file,
                          var.names = varnames,
                          group1.values = data.frame(PHENGRP=c("Stage IV, untreated")),
                          group2.values = data.frame(PHENGRP=c("Non-acute, Stage I, untreated","Stage II-III, untreated")),
                          output.dir=output.folder,
                          suffix.name="analysis3"
                          )
There are  23  and  74  samples for group1 and group2, respectively.
# generate the files for analysis 4. If we directly specify group1.values using numbers, it won't work. I had to specify them to be a space+number as a character.
varnames<-c("nodule_lymph_pheno","steroid_atv1","dmard_atv1")
temp<-my_gsea_filecreate_binary(fpkm.filepath=fpkm.file,
                          clinic.filepath = clinic.file,
                          var.names = varnames,
                          group1.values = data.frame(nodule_lymph_pheno=c("lymph"),steroid_atv1=c(" 0"),dmard_atv1=c(" 0")),
                          group2.values = data.frame(nodule_lymph_pheno=c("micronodule","both"),steroid_atv1=c(" 0"," 0"),dmard_atv1=c(" 0"," 0")),
                          output.dir=output.folder,
                          suffix.name="analysis4"
                          )
There are  43  and  75  samples for group1 and group2, respectively.
# generate the files for analysis 5. 
varnames<-c("PHENGRP")
temp<-my_gsea_filecreate_binary(fpkm.filepath=fpkm.file,
                          clinic.filepath = clinic.file,
                          var.names = varnames,
                          group1.values = data.frame(PHENGRP=c("Remitting, untreated")),
                          group2.values = data.frame(PHENGRP=c("Stage II-III, untreated")),
                          output.dir=output.folder,
                          suffix.name="analysis5"
                          )
There are  40  and  45  samples for group1 and group2, respectively.
# generate the files for analysis 4a. If we directly specify group1.values using numbers, it won't work. I had to specify them to be a space+number as a character.
clinic.filepath<-"/home/yanxiting/driver_Grace/scratch/GRADS/SARC_results/Results_summary_PBMC_hg38/baseline/data/clinic_ct_merge_matrix_withgex.RDS"
clinic.matrix<-readRDS(clinic.filepath,refhook = NULL)
clinic.file<-"/home/yanxiting/driver_Grace/scratch/GRADS/SARC_results/Results_summary_PBMC_hg38/baseline/data/clinic_ct_merge_matrix_withgex.txt"

varnames<-c("nodule_lymph_pheno","steroid_atv1","dmard_atv1","Ground_Glass","Honeycombing","Reticular_Abnormality")
temp<-my_gsea_filecreate_binary(fpkm.filepath=fpkm.file,
                          clinic.filepath = clinic.file,
                          var.names = varnames,
                          group1.values = data.frame(nodule_lymph_pheno=c("lymph"),steroid_atv1=c(" 0"),dmard_atv1=c(" 0"),Ground_Glass=c(" 0"),Honeycombing=c(" 0"),Reticular_Abnormality=c(" 0")),
                          group2.values = data.frame(nodule_lymph_pheno=c("micronodule","both"),steroid_atv1=c(" 0"," 0"),dmard_atv1=c(" 0"," 0"),Ground_Glass=c(" 0"," 0"),Honeycombing=c(" 0"," 0"),Reticular_Abnormality=c(" 0"," 0")),
                          output.dir=output.folder,
                          suffix.name="analysis4a"
                          )
There are  31  and  38  samples for group1 and group2, respectively.
# generate the files for analysis 4b. If we directly specify group1.values using numbers, it won't work. I had to specify them to be a space+number as a character.
clinic.filepath<-"/home/yanxiting/driver_Grace/scratch/GRADS/SARC_results/Results_summary_PBMC_hg38/baseline/data/clinic_ct_merge_matrix_withgex.RDS"
clinic.matrix<-readRDS(clinic.filepath,refhook = NULL)
clinic.file<-"/home/yanxiting/driver_Grace/scratch/GRADS/SARC_results/Results_summary_PBMC_hg38/baseline/data/clinic_ct_merge_matrix_withgex.txt"

varnames<-c("nodule_lymph_pheno","steroid_atv1","dmard_atv1","Ground_Glass","Honeycombing","Reticular_Abnormality")
temp<-my_gsea_filecreate_binary(fpkm.filepath=fpkm.file,
                          clinic.filepath = clinic.file,
                          var.names = varnames,
                          group1.values = data.frame(nodule_lymph_pheno=c("lymph"),steroid_atv1=c(" 0"),dmard_atv1=c(" 0"),Ground_Glass=c(" 0"),Honeycombing=c(" 0"),Reticular_Abnormality=c(" 0")),
                          group2.values = data.frame(nodule_lymph_pheno=c("micronodule"),steroid_atv1=c(" 0"),dmard_atv1=c(" 0"),Ground_Glass=c(" 0"),Honeycombing=c(" 0"),Reticular_Abnormality=c(" 0")),
                          output.dir=output.folder,
                          suffix.name="analysis4b"
                          )
There are  31  and  12  samples for group1 and group2, respectively.

We also generate GSEA input files for patients from given lists including the following:

  • Stage I, untreated and lymphadenopathy only overlapped patients
  • Stage I, untread only patients
  • lymphadenopathy only patients

These three analysis is to see if the overlapped patients drove the significant results.

grads.id.list<-file.path(home.dir,"scratch/GRADS/SARC_results/Results_summary_PBMC_hg38/baseline/GSEA_knowngenes_DESeq2_log2adjusted/gradsid_overlap_nonoverlap.xlsx")
grads.id.matrix<-read.xls(grads.id.list,sheet=1)
overlap.id<-as.character(grads.id.matrix[,1])[as.character(grads.id.matrix[,1])!=""]
stage1only.id<-as.character(grads.id.matrix[,2])[as.character(grads.id.matrix[,2])!=""]
lymphonly.id<-as.character(grads.id.matrix[,3])[as.character(grads.id.matrix[,3])!=""]


#----------------------------------------------------------------------------------------------------------------
# Part 1: 12 stage I, untreated and lymph only patients
#----------------------------------------------------------------------------------------------------------------
# load in the data
fpkm.file=file.path(home.dir,"scratch/GRADS/SARC_results/Results_summary_PBMC_hg38/baseline/data_adjusted2","DESeq2_normalized_276_clean_log2_celldiffadjusted_withannot.txt")
clinic.file<-"/home/yanxiting/driver_Grace/scratch/GRADS/SARC_results/Results_summary_PBMC_hg38/baseline/data/clinic_matrix_merged.txt"
output.folder<-"/home/yanxiting/driver_Grace/scratch/GRADS/SARC_results/Results_summary_PBMC_hg38/baseline/GSEA_knowngenes_DESeq2_log2adjusted"
suffix.name<-"analysis6"
var.names=c("PHENGRP")
group2.values = data.frame(PHENGRP=c("Stage II-III, untreated","Stage IV, untreated"))

# load in the fpkm matrix
fpkm.matrix<-read.table(fpkm.file,sep="\t",header=T,check.names=F,as.is=TRUE,comment.char = "")
fpkm.matrix.anno<-fpkm.matrix[,1:6]
fpkm.matrix<-fpkm.matrix[,7:ncol(fpkm.matrix)]
  
# load in the clinical data and subset the samples to those listed in the fpkm.matrix
clinic.matrix=read.table(clinic.file,sep="\t",header=T,check.names=F,stringsAsFactors=F)
rownames(clinic.matrix)<-as.matrix(clinic.matrix)[,1] # GRADS ID
clinic.matrix<-clinic.matrix[colnames(fpkm.matrix),]

# based on var.name, group1.values, and group2.values, identify the samples to include in the files.
if(file.exists(output.folder)==F){
  dir.create(output.folder)
}
  
gct.filepath<-file.path(output.folder,paste("gexp_",suffix.name,".gct",sep=""))
cls.filepath<-file.path(output.folder,paste("cls_",suffix.name,".cls",sep=""))
  
# substract samples with var.name equal to the values in group1.values and group2. values
#temp1<-fpkm.matrix[,apply(as.data.frame(clinic.matrix[,var.names]),1,paste,collapse="_")%in%apply(group1.values,1,paste,collapse="_")]
temp1<-fpkm.matrix[,overlap.id]
temp2<-fpkm.matrix[,apply(as.data.frame(clinic.matrix[,var.names]),1,paste,collapse="_")%in%apply(group2.values,1,paste,collapse="_")]

cat("There are ",ncol(temp1)," and ", ncol(temp2)," samples for group1 and group2, respectively.\n")
There are  12  and  68  samples for group1 and group2, respectively.
data.matrix<-cbind(temp1,temp2)
data.matrix<-cbind(fpkm.matrix.anno[,1],rep("na",nrow(data.matrix)),data.matrix)
colnames(data.matrix)[1:2]<-c("NAME","Description")
pheno.vect<-c(rep(0,ncol(temp1)),rep(1,ncol(temp2)))

# output the gene expression data
cmd.out<-"#1.2\n"
cmd.out<-paste(cmd.out,nrow(data.matrix),"\t",ncol(data.matrix)-2,"\n",sep="")
cat(cmd.out,file=gct.filepath,append=F)
write.table(data.matrix,sep="\t",file=gct.filepath,append=T,row.names=F,col.names=T,quote=F)

# output the phenotype file
cmd.out<-paste(ncol(data.matrix)-2," ",2," ",1,"\n",sep="")
cmd.out<-paste(cmd.out,"# group1 group2\n",sep="")
cat(cmd.out,file=cls.filepath,append=F,sep="")
cat(pheno.vect,file=cls.filepath,append=T,sep=" ")
#----------------------------------------------------------------------------------------------------------------

# part 2: stage I only
#----------------------------------------------------------------------------------------------------------------
# load in the data
fpkm.file=file.path(home.dir,"scratch/GRADS/SARC_results/Results_summary_PBMC_hg38/baseline/data_adjusted2","DESeq2_normalized_276_clean_log2_celldiffadjusted_withannot.txt")
clinic.file<-"/home/yanxiting/driver_Grace/scratch/GRADS/SARC_results/Results_summary_PBMC_hg38/baseline/data/clinic_matrix_merged.txt"
output.folder<-"/home/yanxiting/driver_Grace/scratch/GRADS/SARC_results/Results_summary_PBMC_hg38/baseline/GSEA_knowngenes_DESeq2_log2adjusted"
suffix.name<-"analysis7"
var.names=c("PHENGRP")
group2.values = data.frame(PHENGRP=c("Stage II-III, untreated","Stage IV, untreated"))

# load in the fpkm matrix
fpkm.matrix<-read.table(fpkm.file,sep="\t",header=T,check.names=F,as.is=TRUE,comment.char = "")
fpkm.matrix.anno<-fpkm.matrix[,1:6]
fpkm.matrix<-fpkm.matrix[,7:ncol(fpkm.matrix)]
  
# load in the clinical data and subset the samples to those listed in the fpkm.matrix
clinic.matrix=read.table(clinic.file,sep="\t",header=T,check.names=F,stringsAsFactors=F)
rownames(clinic.matrix)<-as.matrix(clinic.matrix)[,1] # GRADS ID
clinic.matrix<-clinic.matrix[colnames(fpkm.matrix),]

# based on var.name, group1.values, and group2.values, identify the samples to include in the files.
if(file.exists(output.folder)==F){
  dir.create(output.folder)
}
  
gct.filepath<-file.path(output.folder,paste("gexp_",suffix.name,".gct",sep=""))
cls.filepath<-file.path(output.folder,paste("cls_",suffix.name,".cls",sep=""))
  
# substract samples with var.name equal to the values in group1.values and group2. values
#temp1<-fpkm.matrix[,apply(as.data.frame(clinic.matrix[,var.names]),1,paste,collapse="_")%in%apply(group1.values,1,paste,collapse="_")]
temp1<-fpkm.matrix[,stage1only.id]
temp2<-fpkm.matrix[,apply(as.data.frame(clinic.matrix[,var.names]),1,paste,collapse="_")%in%apply(group2.values,1,paste,collapse="_")]

cat("There are ",ncol(temp1)," and ", ncol(temp2)," samples for group1 and group2, respectively.\n")
There are  17  and  68  samples for group1 and group2, respectively.
data.matrix<-cbind(temp1,temp2)
data.matrix<-cbind(fpkm.matrix.anno[,1],rep("na",nrow(data.matrix)),data.matrix)
colnames(data.matrix)[1:2]<-c("NAME","Description")
pheno.vect<-c(rep(0,ncol(temp1)),rep(1,ncol(temp2)))

# output the gene expression data
cmd.out<-"#1.2\n"
cmd.out<-paste(cmd.out,nrow(data.matrix),"\t",ncol(data.matrix)-2,"\n",sep="")
cat(cmd.out,file=gct.filepath,append=F)
write.table(data.matrix,sep="\t",file=gct.filepath,append=T,row.names=F,col.names=T,quote=F)

# output the phenotype file
cmd.out<-paste(ncol(data.matrix)-2," ",2," ",1,"\n",sep="")
cmd.out<-paste(cmd.out,"# group1 group2\n",sep="")
cat(cmd.out,file=cls.filepath,append=F,sep="")
cat(pheno.vect,file=cls.filepath,append=T,sep=" ")
#----------------------------------------------------------------------------------------------------------------
# part 3: lymph only
#----------------------------------------------------------------------------------------------------------------
# load in the data
fpkm.file=file.path(home.dir,"scratch/GRADS/SARC_results/Results_summary_PBMC_hg38/baseline/data_adjusted2","DESeq2_normalized_276_clean_log2_celldiffadjusted_withannot.txt")
clinic.file<-"/home/yanxiting/driver_Grace/scratch/GRADS/SARC_results/Results_summary_PBMC_hg38/baseline/data/clinic_matrix_merged.txt"
output.folder<-"/home/yanxiting/driver_Grace/scratch/GRADS/SARC_results/Results_summary_PBMC_hg38/baseline/GSEA_knowngenes_DESeq2_log2adjusted"
suffix.name<-"analysis8"
var.names=c("PHENGRP")
group2.values = data.frame(PHENGRP=c("Stage II-III, untreated","Stage IV, untreated"))

# load in the fpkm matrix
fpkm.matrix<-read.table(fpkm.file,sep="\t",header=T,check.names=F,as.is=TRUE,comment.char = "")
fpkm.matrix.anno<-fpkm.matrix[,1:6]
fpkm.matrix<-fpkm.matrix[,7:ncol(fpkm.matrix)]
  
# load in the clinical data and subset the samples to those listed in the fpkm.matrix
clinic.matrix=read.table(clinic.file,sep="\t",header=T,check.names=F,stringsAsFactors=F)
rownames(clinic.matrix)<-as.matrix(clinic.matrix)[,1] # GRADS ID
clinic.matrix<-clinic.matrix[colnames(fpkm.matrix),]

# based on var.name, group1.values, and group2.values, identify the samples to include in the files.
if(file.exists(output.folder)==F){
  dir.create(output.folder)
}
  
gct.filepath<-file.path(output.folder,paste("gexp_",suffix.name,".gct",sep=""))
cls.filepath<-file.path(output.folder,paste("cls_",suffix.name,".cls",sep=""))
  
# substract samples with var.name equal to the values in group1.values and group2. values
#temp1<-fpkm.matrix[,apply(as.data.frame(clinic.matrix[,var.names]),1,paste,collapse="_")%in%apply(group1.values,1,paste,collapse="_")]
temp1<-fpkm.matrix[,lymphonly.id]
temp2<-fpkm.matrix[,apply(as.data.frame(clinic.matrix[,var.names]),1,paste,collapse="_")%in%apply(group2.values,1,paste,collapse="_")]

cat("There are ",ncol(temp1)," and ", ncol(temp2)," samples for group1 and group2, respectively.\n")
There are  33  and  68  samples for group1 and group2, respectively.
data.matrix<-cbind(temp1,temp2)
data.matrix<-cbind(fpkm.matrix.anno[,1],rep("na",nrow(data.matrix)),data.matrix)
colnames(data.matrix)[1:2]<-c("NAME","Description")
pheno.vect<-c(rep(0,ncol(temp1)),rep(1,ncol(temp2)))

# output the gene expression data
cmd.out<-"#1.2\n"
cmd.out<-paste(cmd.out,nrow(data.matrix),"\t",ncol(data.matrix)-2,"\n",sep="")
cat(cmd.out,file=gct.filepath,append=F)
write.table(data.matrix,sep="\t",file=gct.filepath,append=T,row.names=F,col.names=T,quote=F)

# output the phenotype file
cmd.out<-paste(ncol(data.matrix)-2," ",2," ",1,"\n",sep="")
cmd.out<-paste(cmd.out,"# group1 group2\n",sep="")
cat(cmd.out,file=cls.filepath,append=F,sep="")
cat(pheno.vect,file=cls.filepath,append=T,sep=" ")
#----------------------------------------------------------------------------------------------------------------

We examine the CT features of these three groups of patients.

# load in the list of GRADS IDs in all three groups
grads.id.list<-file.path(home.dir,"scratch/GRADS/SARC_results/Results_summary_PBMC_hg38/baseline/GSEA_knowngenes_DESeq2_log2adjusted/gradsid_overlap_nonoverlap.xlsx")
grads.id.matrix<-read.xls(grads.id.list,sheet=1)
overlap.id<-as.character(grads.id.matrix[,1])[as.character(grads.id.matrix[,1])!=""]
stage1only.id<-as.character(grads.id.matrix[,2])[as.character(grads.id.matrix[,2])!=""]
lymphonly.id<-as.character(grads.id.matrix[,3])[as.character(grads.id.matrix[,3])!=""]

# load in the clinical data
clinic.filepath<-file.path(home.dir,"scratch/GRADS/SARC_results/Results_summary_PBMC_hg38/baseline/data/clinic_matrix_merged.RDS")
clinic.matrix<-readRDS(clinic.filepath,refhook = NULL)
clinic.matrix<-clinic.matrix[,c("GENDER","RACE","ethn","AGE","ethor","wbc","cd4","cal","d25","d125","crp","p_lymph","p_mono","p_neut","p_eos","p_baso","FVCPRED","FEV1PRED","PREDDLCO","SCADDING","smoke","pk_yr","steroid_atv1","dmard_atv1","Micronodule","Med_Lymphadenopathy","Hilar_Lymphadenopathy","nodule_lymph_pheno")]
clinic.matrix<-clinic.matrix[c(overlap.id,stage1only.id,lymphonly.id),]

# generate the summary table

var.names<-c("GENDER","RACE","AGE","ethor","wbc","cd4","cal","d25","d125","crp","p_lymph","p_mono","p_neut","p_eos","p_baso","FVCPRED","FEV1PRED","PREDDLCO","SCADDING","smoke","pk_yr","steroid_atv1","dmard_atv1","Micronodule","Med_Lymphadenopathy","Hilar_Lymphadenopathy","nodule_lymph_pheno")

var.names.cat<-c("GENDER","RACE","ethor","smoke","steroid_atv1","dmard_atv1","SCADDING","Micronodule","Med_Lymphadenopathy","Hilar_Lymphadenopathy","nodule_lymph_pheno")
var.names.con<-c("AGE","wbc","cd4","cal","d25","d125","crp","p_lymph","p_mono","p_neut","p_eos","p_baso","FVCPRED","FEV1PRED","PREDDLCO","pk_yr")

var.type<-rep("con",length(var.names))
var.type[var.names%in%var.names.cat]<-"cat"
var.annot<-var.names

summary.table<-character()

for(i in 1:length(var.names)){
  if(var.type[i]=="cat"){
    temp.matrix<-as.matrix(table(clinic.matrix[overlap.id,var.names[i]]))
    rownames(temp.matrix)<-paste(var.annot[i]," - ",rownames(temp.matrix))
    
    temp.sum<-sum(temp.matrix)
    temp.matrix2<-temp.matrix/temp.sum
    summary.table<-rbind(summary.table,as.matrix(paste(temp.matrix,"(",round(temp.matrix2,digits=2),")",sep="")))
    rownames(summary.table)[(nrow(summary.table)-nrow(temp.matrix)+1):nrow(summary.table)]<-rownames(temp.matrix)
    }else{
    temp.mean<-mean(clinic.matrix[overlap.id,var.names[i]],na.rm=T)
    temp.sd<-sd(clinic.matrix[overlap.id,var.names[i]],na.rm=T)
    temp.mean<-round(temp.mean,digits=2)
    temp.sd<-round(temp.sd,digits=2)
    summary.table<-rbind(summary.table,paste(temp.mean,"±",temp.sd,sep=""))
    rownames(summary.table)[nrow(summary.table)]<-paste(var.annot[i],", mean±SD",sep="")
  }
  
}

colnames(summary.table)[1]<-"Overlap"
summary.table.total<-summary.table


summary.table<-character()

for(i in 1:length(var.names)){
  if(var.type[i]=="cat"){
    temp.matrix<-as.matrix(table(clinic.matrix[stage1only.id,var.names[i]]))
    rownames(temp.matrix)<-paste(var.annot[i]," - ",rownames(temp.matrix))
    
    temp.sum<-sum(temp.matrix)
    temp.matrix2<-temp.matrix/temp.sum
    summary.table<-rbind(summary.table,as.matrix(paste(temp.matrix,"(",round(temp.matrix2,digits=2),")",sep="")))
    rownames(summary.table)[(nrow(summary.table)-nrow(temp.matrix)+1):nrow(summary.table)]<-rownames(temp.matrix)
    }else{
    temp.mean<-mean(clinic.matrix[stage1only.id,var.names[i]],na.rm=T)
    temp.sd<-sd(clinic.matrix[stage1only.id,var.names[i]],na.rm=T)
    temp.mean<-round(temp.mean,digits=2)
    temp.sd<-round(temp.sd,digits=2)
    summary.table<-rbind(summary.table,paste(temp.mean,"±",temp.sd,sep=""))
    rownames(summary.table)[nrow(summary.table)]<-paste(var.annot[i],", mean±SD",sep="")
  }
  
}

colnames(summary.table)[1]<-"Stage I only"

temp.names<-union(rownames(summary.table.total),rownames(summary.table))
temp<-matrix("",nrow=length(temp.names),ncol=2)
rownames(temp)<-temp.names
temp[rownames(summary.table.total),1]<-summary.table.total[,1]
temp[rownames(summary.table),2]<-summary.table[,1]
summary.table.total<-temp


summary.table<-character()

for(i in 1:length(var.names)){
  if(var.type[i]=="cat"){
    temp.matrix<-as.matrix(table(clinic.matrix[lymphonly.id,var.names[i]]))
    rownames(temp.matrix)<-paste(var.annot[i]," - ",rownames(temp.matrix))
    
    temp.sum<-sum(temp.matrix)
    temp.matrix2<-temp.matrix/temp.sum
    summary.table<-rbind(summary.table,as.matrix(paste(temp.matrix,"(",round(temp.matrix2,digits=2),")",sep="")))
    rownames(summary.table)[(nrow(summary.table)-nrow(temp.matrix)+1):nrow(summary.table)]<-rownames(temp.matrix)
    }else{
    temp.mean<-mean(clinic.matrix[lymphonly.id,var.names[i]],na.rm=T)
    temp.sd<-sd(clinic.matrix[lymphonly.id,var.names[i]],na.rm=T)
    temp.mean<-round(temp.mean,digits=2)
    temp.sd<-round(temp.sd,digits=2)
    summary.table<-rbind(summary.table,paste(temp.mean,"±",temp.sd,sep=""))
    rownames(summary.table)[nrow(summary.table)]<-paste(var.annot[i],", mean±SD",sep="")
  }
  
}

colnames(summary.table)[1]<-"Lymph only"

temp.names<-union(rownames(summary.table.total),rownames(summary.table))
temp<-matrix("",nrow=length(temp.names),ncol=3)
rownames(temp)<-temp.names
temp[rownames(summary.table.total),1]<-summary.table.total[,1]
temp[rownames(summary.table.total),2]<-summary.table.total[,2]
temp[rownames(summary.table),3]<-summary.table[,1]

summary.table.total<-temp

colnames(summary.table.total)<-c("overlap","Stage 1 only","Lymph only")


# calculate the p values to assess the differences between the three groups of patients for each trait

pvalue.vect<-numeric()

for(i in 1:length(var.names)){
  
  if(var.type[i]=="cat"){
    temp.matrix<-as.matrix(table(clinic.matrix[c(overlap.id,stage1only.id,lymphonly.id),var.names[i]]))
    rownames(temp.matrix)<-paste(var.annot[i]," - ",rownames(temp.matrix))
    
    temp1<-clinic.matrix[c(overlap.id,stage1only.id,lymphonly.id),var.names[i]]
    temp2<-c(rep("overlap",length(overlap.id)),rep("stage1",length(stage1only.id)),rep("lymph",length(lymphonly.id)))
    temp.index<-1:length(temp1)
    temp.index<-temp.index[!(is.na(temp1) | is.na(temp2))]
    temp1<-temp1[temp.index]
    temp2<-temp2[temp.index]
    
    if(length(unique(temp1))==1){
      pvalue.vect<-c(pvalue.vect, rep(1,length(unique(temp1))))
      names(pvalue.vect)[length(pvalue.vect)]<-rownames(temp.matrix)
    }else{
      pvalue.vect<-c(pvalue.vect, rep(chisq.test(temp1,as.factor(temp2))$p.value,length(unique(temp1))))  
      names(pvalue.vect)[(length(pvalue.vect)-length(unique(temp1))+1):length(pvalue.vect)]<-rownames(temp.matrix)
    }
    
    
    }else{
    temp1<-clinic.matrix[c(overlap.id,stage1only.id,lymphonly.id),var.names[i]]
    temp2<-c(rep("overlap",length(overlap.id)),rep("stage1",length(stage1only.id)),rep("lymph",length(lymphonly.id)))
    pvalue.vect<-c(pvalue.vect, kruskal.test(temp1~as.factor(temp2))$p.value)
    names(pvalue.vect)[length(pvalue.vect)]<-paste(var.annot[i],", mean±SD",sep="")
  }
  
}

summary.table.total<-cbind(summary.table.total,pvalue.vect[rownames(summary.table.total)])
colnames(summary.table.total)[4]<-"p value"

output.filepath<-file.path(home.dir,"scratch/GRADS/SARC_results/Results_summary_PBMC_hg38/baseline/GSEA_knowngenes_DESeq2_log2adjusted/gradsid_overlap_nonoverlap_clinicfeatures.xlsx")
write.xlsx(summary.table.total,file=output.filepath,row.names=T,col.names=T,append=F)

We also generate the feature table for the CT scan variables.

# load in the list of GRADS IDs in all three groups
grads.id.list<-file.path(home.dir,"scratch/GRADS/SARC_results/Results_summary_PBMC_hg38/baseline/GSEA_knowngenes_DESeq2_log2adjusted/gradsid_overlap_nonoverlap.xlsx")
grads.id.matrix<-read.xls(grads.id.list,sheet=1)
overlap.id<-as.character(grads.id.matrix[,1])[as.character(grads.id.matrix[,1])!=""]
stage1only.id<-as.character(grads.id.matrix[,2])[as.character(grads.id.matrix[,2])!=""]
lymphonly.id<-as.character(grads.id.matrix[,3])[as.character(grads.id.matrix[,3])!=""]

# load in the clinical data
ct.filepath<-file.path(home.dir,"scratch/GRADS/SARC_results/Data/CT_data_20170712/Sarc_ct_reads_corrected.xls")
ct.matrix<-read.xls(ct.filepath,sheet=1)
rownames(ct.matrix)<-as.matrix(ct.matrix)[,"GRADSID"]
ct.matrix<-ct.matrix[,c("Med_Lymphadenopathy","Hilar_Lymphadenopathy","Micronodule","Bronchial_Wall_Thickening","Traction_Bronchiectasis","Bronchiectasis_severity","Ground_Glass","Honeycombing","Reticular_Abnormality","Pulmonary_Art","Tree_in_bud")]

ct.matrix<-ct.matrix[c(overlap.id,stage1only.id,lymphonly.id),]
group.vect<-c(rep("overlap",length(overlap.id)),rep("Stage I",length(stage1only.id)),rep("Lymph",length(lymphonly.id)))

# recode Med_Lymphadenopathy and Hilar_Lymphadenopathy
temp<-ct.matrix[,"Med_Lymphadenopathy"]
temp1<-rep(0,length(temp))
temp1[temp%in%c(1,2)]<-1
temp1[temp==3]<-2   # 0=no, 1=one side, 2=billateral
ct.matrix[,"Med_Lymphadenopathy"]<-temp1


temp<-ct.matrix[,"Hilar_Lymphadenopathy"]
temp1<-rep(0,length(temp))
temp1[temp%in%c(1,2)]<-1
temp1[temp==3]<-2   # 0=no, 1=one side, 2=billateral
ct.matrix[,"Hilar_Lymphadenopathy"]<-temp1


# generate the summary table
var.names<-c("Med_Lymphadenopathy","Hilar_Lymphadenopathy","Micronodule","Bronchial_Wall_Thickening","Traction_Bronchiectasis","Bronchiectasis_severity","Ground_Glass","Honeycombing","Reticular_Abnormality","Pulmonary_Art","Tree_in_bud")

summary.table<-character()

for(i in 1:length(var.names)){
  
    temp.matrix<-as.matrix(table(ct.matrix[overlap.id,var.names[i]]))
    rownames(temp.matrix)<-paste(var.names[i]," - ",rownames(temp.matrix))
    
    temp.sum<-sum(temp.matrix)
    temp.matrix2<-temp.matrix/temp.sum
    summary.table<-rbind(summary.table,as.matrix(paste(temp.matrix,"(",round(temp.matrix2,digits=2),")",sep="")))
    rownames(summary.table)[(nrow(summary.table)-nrow(temp.matrix)+1):nrow(summary.table)]<-rownames(temp.matrix)
}

colnames(summary.table)[1]<-"Overlap"
summary.table.total<-summary.table

summary.table<-character()

for(i in 1:length(var.names)){
  
    temp.matrix<-as.matrix(table(ct.matrix[stage1only.id,var.names[i]]))
    rownames(temp.matrix)<-paste(var.names[i]," - ",rownames(temp.matrix))
    
    temp.sum<-sum(temp.matrix)
    temp.matrix2<-temp.matrix/temp.sum
    summary.table<-rbind(summary.table,as.matrix(paste(temp.matrix,"(",round(temp.matrix2,digits=2),")",sep="")))
    rownames(summary.table)[(nrow(summary.table)-nrow(temp.matrix)+1):nrow(summary.table)]<-rownames(temp.matrix)
}

colnames(summary.table)[1]<-"Stage I"


temp.names<-union(rownames(summary.table.total),rownames(summary.table))
temp<-matrix("",nrow=length(temp.names),ncol=2)
rownames(temp)<-temp.names
temp[rownames(summary.table.total),1]<-summary.table.total[,1]
temp[rownames(summary.table),2]<-summary.table[,1]
summary.table.total<-temp


summary.table<-character()

for(i in 1:length(var.names)){
  
    temp.matrix<-as.matrix(table(ct.matrix[lymphonly.id,var.names[i]]))
    rownames(temp.matrix)<-paste(var.names[i]," - ",rownames(temp.matrix))
    
    temp.sum<-sum(temp.matrix)
    temp.matrix2<-temp.matrix/temp.sum
    summary.table<-rbind(summary.table,as.matrix(paste(temp.matrix,"(",round(temp.matrix2,digits=2),")",sep="")))
    rownames(summary.table)[(nrow(summary.table)-nrow(temp.matrix)+1):nrow(summary.table)]<-rownames(temp.matrix)
}

colnames(summary.table)[1]<-"Lymph only"


temp.names<-union(rownames(summary.table.total),rownames(summary.table))
temp<-matrix("",nrow=length(temp.names),ncol=3)
rownames(temp)<-temp.names
temp[rownames(summary.table.total),1]<-summary.table.total[,1]
temp[rownames(summary.table.total),2]<-summary.table.total[,2]
temp[rownames(summary.table),3]<-summary.table[,1]

summary.table.total<-temp

colnames(summary.table.total)<-c("overlap","Stage 1 only","Lymph only")


# calculate the p values to assess the differences between the three groups of patients for each trait

pvalue.vect<-numeric()

for(i in 1:length(var.names)){
  
    temp.matrix<-as.matrix(table(ct.matrix[c(overlap.id,stage1only.id,lymphonly.id),var.names[i]]))
    rownames(temp.matrix)<-paste(var.names[i]," - ",rownames(temp.matrix))
    
    temp1<-ct.matrix[c(overlap.id,stage1only.id,lymphonly.id),var.names[i]]
    temp2<-c(rep("overlap",length(overlap.id)),rep("stage1",length(stage1only.id)),rep("lymph",length(lymphonly.id)))
    temp.index<-1:length(temp1)
    temp.index<-temp.index[!(is.na(temp1) | is.na(temp2))]
    temp1<-temp1[temp.index]
    temp2<-temp2[temp.index]
    
    if(length(unique(temp1))==1){
      pvalue.vect<-c(pvalue.vect, rep(1,length(unique(temp1))))
      names(pvalue.vect)[length(pvalue.vect)]<-rownames(temp.matrix)
    }else{
      pvalue.vect<-c(pvalue.vect, rep(chisq.test(temp1,as.factor(temp2))$p.value,length(unique(temp1))))  
      names(pvalue.vect)[(length(pvalue.vect)-length(unique(temp1))+1):length(pvalue.vect)]<-rownames(temp.matrix)
    }
}

summary.table.total<-cbind(summary.table.total,pvalue.vect[rownames(summary.table.total)])
colnames(summary.table.total)[4]<-"p value"

output.filepath<-file.path(home.dir,"scratch/GRADS/SARC_results/Results_summary_PBMC_hg38/baseline/GSEA_knowngenes_DESeq2_log2adjusted/gradsid_overlap_nonoverlap_ctfeatures.xlsx")
write.xlsx(summary.table.total,file=output.filepath,row.names=T,col.names=T,append=F)

We also generated the GSEA input files for three three groups of patients using the function we wrote.


# output the merged clinical data matrix as a txt file
clinic.filepath<-"/home/yanxiting/driver_Grace/scratch/GRADS/SARC_results/Results_summary_PBMC_hg38/baseline/data/clinic_matrix_merged.RDS"
clinic.matrix<-readRDS(clinic.filepath,refhook = NULL)
output.filepath<-"/home/yanxiting/driver_Grace/scratch/GRADS/SARC_results/Results_summary_PBMC_hg38/baseline/data/clinic_matrix_merged.txt"
write.table(clinic.matrix,file=output.filepath,sep="\t",row.names=F,col.names=T,quote=F,append=F)


# note this function is slightly different for DESeq2 Norm data and the TPM matrix
my_gsea_filecreate_binary<-function(fpkm.filepath,clinic.filepath,var.names,group1.values,group2.values,group1.name="group1",group2.name="group2",output.dir,suffix.name){
  #####################################################################################################################################################
  # Arguments:
  # 
  # gexp.filepath is the file path of the TPM matrix with first 5 columns being annotation for genes 
  # (TPM_baseline_276_clean_celldiffadjusted_withannot.txt).
  # 
  # clinic.filpath is the file path of the clinic data (clinic_matrix_merged.txt). The first row needs to be the column names.
  # 
  # var.names is a vector containing the names of columns in clinic.filepath that you want to generate the phenotype file for.
  #
  # group1.values is a data.frame of string describing values of var.names that are considered as gorup 1. 
  # For example, group1.values=data.frame(PHENGRP="nonacute Stage I untreated"). Note that the columns in group1.values need to match those in var.names if there are
  # more than 1 variable to define the groups.
  #
  # group2.values has the same format as group1.values but is the settings for group2.
  #
  # group1.name is the name you want to call your group1 in the cls file.
  #
  # group2.name is the name you want to call your group2 in the cls file.
  # 
  # output.dir is the folder name where you want to save the created files.
  #
  # suffix.name is the suffix name in the name of the output files. The gct file will be named as gct_suffix.name.gct and cls file will be named as
  # cls_suffix.name.gct. For example, if suffix.name="test", the gct file will be named "gct_test.gct" and the cls file will be named as 
  # "cls_test.cls".
  # 
  # Value:
  #
  # This function will return 0 if successfully ran. Otherwise, it will return 1. If unsuccessful, information regarding what went wrong will be spit 
  # out as warning messages.
  #
  # When ran successfully, this function will create two files under the specified output.dir named gct_var.name.gct and cls_var.name.cls, where 
  # var.name will be the speficied value 
  # in the argument. These two files can be directly loaded into GSEA together with another gene set file that needs to be generated outside of this 
  # function.
  #####################################################################################################################################################
  
  #  generate the gene expression matrix input file for GSEA
  #fpkm.filepath<-file.path(home.dir,"scratch/GRADS/SARC_results/Results_summary_PBMC_hg38/baseline/data_adjusted2","TPM_baseline_276_clean_celldiffadjusted_withannot.txt")
  #clinic.filepath<-"/home/yanxiting/driver_Grace/scratch/GRADS/SARC_results/Results_summary_PBMC_hg38/baseline/data/clinic_matrix_merged.txt"
  #output.dir<-"/home/yanxiting/driver_Grace/scratch/GRADS/SARC_results/Results_summary_PBMC_hg38/baseline/GSEA_knowngenes"
  # suffix.name<-"analysis1"
  
  
  # load in the fpkm matrix
  fpkm.matrix<-read.table(fpkm.filepath,sep="\t",header=T,check.names=F,as.is=TRUE,comment.char = "")
  fpkm.matrix.anno<-fpkm.matrix[,1:6]
  fpkm.matrix<-fpkm.matrix[,7:ncol(fpkm.matrix)]
  
  # load in the clinical data and subset the samples to those listed in the fpkm.matrix
  clinic.matrix=read.table(clinic.filepath,sep="\t",header=T,check.names=F,stringsAsFactors=F)
  rownames(clinic.matrix)<-as.matrix(clinic.matrix)[,1] # GRADS ID
  clinic.matrix<-clinic.matrix[colnames(fpkm.matrix),]

  # based on var.name, group1.values, and group2.values, identify the samples to include in the files.
  if(file.exists(output.dir)==F){
    dir.create(output.dir)
  }
  
  gct.filepath<-file.path(output.dir,paste("gexp_",suffix.name,".gct",sep=""))
  cls.filepath<-file.path(output.dir,paste("cls_",suffix.name,".cls",sep=""))
  
  # substract samples with var.name equal to the values in group1.values and group2. values
  temp1<-fpkm.matrix[,apply(as.data.frame(clinic.matrix[,var.names]),1,paste,collapse="_")%in%apply(group1.values,1,paste,collapse="_")]
  temp2<-fpkm.matrix[,apply(as.data.frame(clinic.matrix[,var.names]),1,paste,collapse="_")%in%apply(group2.values,1,paste,collapse="_")]
  cat("There are ",ncol(temp1)," and ", ncol(temp2)," samples for group1 and group2, respectively.\n")
  data.matrix<-cbind(temp1,temp2)
  data.matrix<-cbind(fpkm.matrix.anno[,1],rep("na",nrow(data.matrix)),data.matrix)
  colnames(data.matrix)[1:2]<-c("NAME","Description")
  pheno.vect<-c(rep(0,ncol(temp1)),rep(1,ncol(temp2)))
  
  # output the gene expression data
  cmd.out<-"#1.2\n"
  cmd.out<-paste(cmd.out,nrow(data.matrix),"\t",ncol(data.matrix)-2,"\n",sep="")
  cat(cmd.out,file=gct.filepath,append=F)
  write.table(data.matrix,file=gct.filepath,append=T,row.names=F,col.names=T,quote=F)
  
  # output the phenotype file
  cmd.out<-paste(ncol(data.matrix)-2,"\t",2,"\t",1,"\n",sep="")
  cmd.out<-paste(cmd.out,"# group1 group2\n",sep="")
  cat(cmd.out,file=cls.filepath,append=F,sep="")
  cat(pheno.vect,file=cls.filepath,append=T,sep=" ")
  
  return(0)
}



fpkm.file=file.path(home.dir,"scratch/GRADS/SARC_results/Results_summary_PBMC_hg38/baseline/data_adjusted2","DESeq2_normalized_276_clean_log2_celldiffadjusted_withannot.txt")
clinic.file<-"/home/yanxiting/driver_Grace/scratch/GRADS/SARC_results/Results_summary_PBMC_hg38/baseline/data/clinic_matrix_merged.txt"
output.folder<-"/home/yanxiting/driver_Grace/scratch/GRADS/SARC_results/Results_summary_PBMC_hg38/baseline/GSEA_knowngenes_DESeq2_log2adjusted"

# generate the files for analysis 9: overlapped patients
varnames<-c("PHENGRP","nodule_lymph_pheno")
my_gsea_filecreate_binary(fpkm.filepath=fpkm.file,
                          clinic.filepath = clinic.file,
                          var.names = varnames,
                          group1.values = data.frame(PHENGRP=c("Non-acute, Stage I, untreated"),nodule_lymph_pheno=c("lymph")),
                          group2.values = data.frame(PHENGRP=c("Stage II-III, untreated","Stage II-III, untreated","Stage II-III, untreated","Stage IV, untreated","Stage IV, untreated","Stage IV, untreated"),nodule_lymph_pheno=c("lymph","micronodule","both","lymph","micronodule","both")),
                          output.dir=output.folder,
                          suffix.name="analysis9"
                          )

# generate the files for analysis 10: Stage I only
varnames<-c("PHENGRP","nodule_lymph_pheno")
my_gsea_filecreate_binary(fpkm.filepath=fpkm.file,
                          clinic.filepath = clinic.file,
                          var.names = varnames,
                          group1.values = data.frame(PHENGRP=c("Non-acute, Stage I, untreated","Non-acute, Stage I, untreated"),nodule_lymph_pheno=c("micronodule","both")),
                          group2.values = data.frame(PHENGRP=c("Stage II-III, untreated","Stage II-III, untreated","Stage II-III, untreated","Stage IV, untreated","Stage IV, untreated","Stage IV, untreated"),nodule_lymph_pheno=c("lymph","micronodule","both","lymph","micronodule","both")),
                          output.dir=output.folder,
                          suffix.name="analysis10"
                          )

# generate the files for analysis 11: lymph only (no stage I)
varnames<-c("PHENGRP","nodule_lymph_pheno")
phen.value<-c("Non-acute, Stage I, untreated","Cardiac defining therapy","Stage IV, treated","Stage IV, untreated","Stage II-III, untreated","Remitting, untreated","Multi-organ", "Stage II-III, treated","Acute Sarcoidosis, untreated")
phen.value<-setdiff(phen.value,"Non-acute, Stage I, untreated")

my_gsea_filecreate_binary(fpkm.filepath=fpkm.file,
                          clinic.filepath = clinic.file,
                          var.names = varnames,
                          group1.values = data.frame(PHENGRP=phen.value,nodule_lymph_pheno=rep("lymph",length(phen.value))),
                          group2.values = data.frame(PHENGRP=c("Stage II-III, untreated","Stage II-III, untreated","Stage II-III, untreated","Stage IV, untreated","Stage IV, untreated","Stage IV, untreated"),nodule_lymph_pheno=c("lymph","micronodule","both","lymph","micronodule","both")),
                          output.dir=output.folder,
                          suffix.name="analysis11"
                          )

Comparing to 10X Genomicx data

We downloaded a previously published 10X Genomicx data and compared the genes in Bloom data to see if they are enriched in cell type markers or which cell types are expressing them.

Standard Seurat

First, we load in the raw count data and redo the preprocessing using standard Seurat pipeline.

library(Matrix)
count.filepath<-"/home/yanxiting/Documents/Research/GRADS_SARC_PBMC/Data/GSE132338_matrix_counts.mtx"
coldata.filepath<-"/home/yanxiting/Documents/Research/GRADS_SARC_PBMC/Data/GSE132338_matrix_colData.txt"
rowdata.filepath<-"/home/yanxiting/Documents/Research/GRADS_SARC_PBMC/Data/GSE132338_matrix_rowData.mtx"
data.filepath<-"/home/yanxiting/Documents/Research/GRADS_SARC_PBMC/Data/GSE132338_SingleCellExperiment.RDS"
sc.data<-readRDS(data.filepath,refhook=NULL)

count.data<-readMM(count.filepath)
mycol.data<-read.table(coldata.filepath,sep="\t",row.names=1,header=T)
myrow.data<-readLines(rowdata.filepath)

# create a seurat object with the count data and put the integrated data in
rownames(count.data)<-myrow.data[2:length(myrow.data)]
colnames(count.data)<-rownames(mycol.data)

# create a Seurat object using the count matrix
my.data<-CreateSeuratObject(counts=count.data,project="PBMC",min.cells=0,min.features = 0)
my.data[["percent.mt"]] <- PercentageFeatureSet(my.data, pattern = "^MT-")

# Visualize QC metrics as a violin plot
VlnPlot(my.data, features = c("nFeature_RNA", "nCount_RNA", "percent.mt"), ncol = 3)

plot1 <- FeatureScatter(my.data, feature1 = "nCount_RNA", feature2 = "percent.mt")
plot2 <- FeatureScatter(my.data, feature1 = "nCount_RNA", feature2 = "nFeature_RNA")
grid.arrange(plot1 , plot2,ncol=2)


my.data <- NormalizeData(my.data, normalization.method = "LogNormalize", scale.factor = 10000)
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
my.data <- FindVariableFeatures(my.data, selection.method = "vst", nfeatures = 2000)
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
# Identify the 10 most highly variable genes
top10 <- head(VariableFeatures(my.data), 10)

# plot variable features with and without labels
plot1 <- VariableFeaturePlot(my.data)
plot2 <- LabelPoints(plot = plot1, points = top10, repel = TRUE)
When using repel, set xnudge and ynudge to 0 for optimal results
grid.arrange(plot1,plot2,ncol=2)

# decide the number of PC to use
my.data <- JackStraw(my.data, dims = 40,num.replicate = 100)

  |                                                  | 0 % ~calculating  
  |+                                                 | 1 % ~01h 12m 28s  
  |+                                                 | 2 % ~01h 12m 49s  
  |++                                                | 3 % ~01h 13m 21s  
  |++                                                | 4 % ~01h 12m 38s  
  |+++                                               | 5 % ~01h 11m 49s  
  |+++                                               | 6 % ~01h 12m 03s  
  |++++                                              | 7 % ~01h 11m 09s  
  |++++                                              | 8 % ~01h 10m 10s  
  |+++++                                             | 9 % ~01h 09m 29s  
  |+++++                                             | 10% ~01h 08m 22s  
  |++++++                                            | 11% ~01h 07m 50s  
  |++++++                                            | 12% ~01h 06m 43s  
  |+++++++                                           | 13% ~01h 05m 50s  
  |+++++++                                           | 14% ~01h 04m 54s  
  |++++++++                                          | 15% ~01h 04m 20s  
  |++++++++                                          | 16% ~01h 03m 37s  
  |+++++++++                                         | 17% ~01h 02m 43s  
  |+++++++++                                         | 18% ~01h 01m 58s  
  |++++++++++                                        | 19% ~01h 01m 22s  
  |++++++++++                                        | 20% ~01h 00m 22s  
  |+++++++++++                                       | 21% ~59m 33s      
  |+++++++++++                                       | 22% ~58m 50s      
  |++++++++++++                                      | 23% ~58m 02s      
  |++++++++++++                                      | 24% ~57m 13s      
  |+++++++++++++                                     | 25% ~56m 28s      
  |+++++++++++++                                     | 26% ~56m 12s      
  |++++++++++++++                                    | 27% ~55m 24s      
  |++++++++++++++                                    | 28% ~54m 29s      
  |+++++++++++++++                                   | 29% ~53m 52s      
  |+++++++++++++++                                   | 30% ~53m 08s      
  |++++++++++++++++                                  | 31% ~52m 22s      
  |++++++++++++++++                                  | 32% ~51m 32s      
  |+++++++++++++++++                                 | 33% ~50m 43s      
  |+++++++++++++++++                                 | 34% ~50m 01s      
  |++++++++++++++++++                                | 35% ~49m 17s      
  |++++++++++++++++++                                | 36% ~48m 31s      
  |+++++++++++++++++++                               | 37% ~47m 48s      
  |+++++++++++++++++++                               | 38% ~46m 59s      
  |++++++++++++++++++++                              | 39% ~46m 31s      
  |++++++++++++++++++++                              | 40% ~46m 03s      
  |+++++++++++++++++++++                             | 41% ~45m 25s      
  |+++++++++++++++++++++                             | 42% ~44m 38s      
  |++++++++++++++++++++++                            | 43% ~43m 49s      
  |++++++++++++++++++++++                            | 44% ~43m 04s      
  |+++++++++++++++++++++++                           | 45% ~42m 18s      
  |+++++++++++++++++++++++                           | 46% ~41m 28s      
  |++++++++++++++++++++++++                          | 47% ~40m 48s      
  |++++++++++++++++++++++++                          | 48% ~39m 59s      
  |+++++++++++++++++++++++++                         | 49% ~39m 11s      
  |+++++++++++++++++++++++++                         | 50% ~38m 26s      
  |++++++++++++++++++++++++++                        | 51% ~37m 44s      
  |++++++++++++++++++++++++++                        | 52% ~36m 56s      
  |+++++++++++++++++++++++++++                       | 53% ~36m 07s      
  |+++++++++++++++++++++++++++                       | 54% ~35m 24s      
  |++++++++++++++++++++++++++++                      | 55% ~34m 36s      
  |++++++++++++++++++++++++++++                      | 56% ~33m 49s      
  |+++++++++++++++++++++++++++++                     | 57% ~33m 01s      
  |+++++++++++++++++++++++++++++                     | 58% ~32m 12s      
  |++++++++++++++++++++++++++++++                    | 59% ~31m 26s      
  |++++++++++++++++++++++++++++++                    | 60% ~30m 39s      
  |+++++++++++++++++++++++++++++++                   | 61% ~29m 52s      
  |+++++++++++++++++++++++++++++++                   | 62% ~29m 04s      
  |++++++++++++++++++++++++++++++++                  | 63% ~28m 15s      
  |++++++++++++++++++++++++++++++++                  | 64% ~27m 28s      
  |+++++++++++++++++++++++++++++++++                 | 65% ~26m 42s      
  |+++++++++++++++++++++++++++++++++                 | 66% ~25m 55s      
  |++++++++++++++++++++++++++++++++++                | 67% ~25m 09s      
  |++++++++++++++++++++++++++++++++++                | 68% ~24m 24s      
  |+++++++++++++++++++++++++++++++++++               | 69% ~23m 38s      
  |+++++++++++++++++++++++++++++++++++               | 70% ~22m 51s      
  |++++++++++++++++++++++++++++++++++++              | 71% ~22m 07s      
  |++++++++++++++++++++++++++++++++++++              | 72% ~21m 21s      
  |+++++++++++++++++++++++++++++++++++++             | 73% ~20m 34s      
  |+++++++++++++++++++++++++++++++++++++             | 74% ~19m 47s      
  |++++++++++++++++++++++++++++++++++++++            | 75% ~19m 01s      
  |++++++++++++++++++++++++++++++++++++++            | 76% ~18m 14s      
  |+++++++++++++++++++++++++++++++++++++++           | 77% ~17m 28s      
  |+++++++++++++++++++++++++++++++++++++++           | 78% ~16m 42s      
  |++++++++++++++++++++++++++++++++++++++++          | 79% ~15m 56s      
  |++++++++++++++++++++++++++++++++++++++++          | 80% ~15m 11s      
  |+++++++++++++++++++++++++++++++++++++++++         | 81% ~14m 25s      
  |+++++++++++++++++++++++++++++++++++++++++         | 82% ~13m 40s      
  |++++++++++++++++++++++++++++++++++++++++++        | 83% ~12m 54s      
  |++++++++++++++++++++++++++++++++++++++++++        | 84% ~12m 09s      
  |+++++++++++++++++++++++++++++++++++++++++++       | 85% ~11m 23s      
  |+++++++++++++++++++++++++++++++++++++++++++       | 86% ~10m 37s      
  |++++++++++++++++++++++++++++++++++++++++++++      | 87% ~09m 51s      
  |++++++++++++++++++++++++++++++++++++++++++++      | 88% ~09m 05s      
  |+++++++++++++++++++++++++++++++++++++++++++++     | 89% ~08m 19s      
  |+++++++++++++++++++++++++++++++++++++++++++++     | 90% ~07m 34s      
  |++++++++++++++++++++++++++++++++++++++++++++++    | 91% ~06m 49s      
  |++++++++++++++++++++++++++++++++++++++++++++++    | 92% ~06m 03s      
  |+++++++++++++++++++++++++++++++++++++++++++++++   | 93% ~05m 18s      
  |+++++++++++++++++++++++++++++++++++++++++++++++   | 94% ~04m 32s      
  |++++++++++++++++++++++++++++++++++++++++++++++++  | 95% ~03m 47s      
  |++++++++++++++++++++++++++++++++++++++++++++++++  | 96% ~03m 02s      
  |+++++++++++++++++++++++++++++++++++++++++++++++++ | 97% ~02m 16s      
  |+++++++++++++++++++++++++++++++++++++++++++++++++ | 98% ~01m 31s      
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 99% ~45s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 100% elapsed=01h 15m 37s

  |                                                  | 0 % ~calculating  
  |++                                                | 2 % ~02s          
  |+++                                               | 5 % ~02s          
  |++++                                              | 8 % ~02s          
  |+++++                                             | 10% ~02s          
  |+++++++                                           | 12% ~02s          
  |++++++++                                          | 15% ~01s          
  |+++++++++                                         | 18% ~01s          
  |++++++++++                                        | 20% ~01s          
  |++++++++++++                                      | 22% ~01s          
  |+++++++++++++                                     | 25% ~01s          
  |++++++++++++++                                    | 28% ~01s          
  |+++++++++++++++                                   | 30% ~01s          
  |+++++++++++++++++                                 | 32% ~01s          
  |++++++++++++++++++                                | 35% ~01s          
  |+++++++++++++++++++                               | 38% ~01s          
  |++++++++++++++++++++                              | 40% ~01s          
  |++++++++++++++++++++++                            | 42% ~01s          
  |+++++++++++++++++++++++                           | 45% ~01s          
  |++++++++++++++++++++++++                          | 48% ~01s          
  |+++++++++++++++++++++++++                         | 50% ~01s          
  |+++++++++++++++++++++++++++                       | 52% ~01s          
  |++++++++++++++++++++++++++++                      | 55% ~01s          
  |+++++++++++++++++++++++++++++                     | 58% ~01s          
  |++++++++++++++++++++++++++++++                    | 60% ~01s          
  |++++++++++++++++++++++++++++++++                  | 62% ~01s          
  |+++++++++++++++++++++++++++++++++                 | 65% ~01s          
  |++++++++++++++++++++++++++++++++++                | 68% ~01s          
  |+++++++++++++++++++++++++++++++++++               | 70% ~01s          
  |+++++++++++++++++++++++++++++++++++++             | 72% ~00s          
  |++++++++++++++++++++++++++++++++++++++            | 75% ~00s          
  |+++++++++++++++++++++++++++++++++++++++           | 78% ~00s          
  |++++++++++++++++++++++++++++++++++++++++          | 80% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++        | 82% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++       | 85% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++      | 88% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++     | 90% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++++   | 92% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++  | 95% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++++++ | 98% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 100% elapsed=02s  
my.data <- ScoreJackStraw(my.data, dims = 1:40)
JackStrawPlot(my.data, dims = 1:40)

We decides to use the top 20 PCs.

Draw the UMAP with annotation from GEO.

DimPlot(my.data, reduction = "umap",shuffle=T)
The following functions and any applicable methods accept the dots: CombinePlots

Save the Seurat object.

saveRDS(my.data, file ="/home/yanxiting/Documents/Research/GRADS_SARC_PBMC/scRNA_compare/GSE132338_seurat_nointegration.rds")

We check the expression of the analysis4b genes in all cell types.

my.distinct.colors<-c('#e6194b' , '#3cb44b', '#ffe119', '#4363d8', '#f58231', '#911eb4', '#46f0f0', '#f032e6', '#bcf60c', '#fabebe', '#008080', '#e6beff', '#9a6324', '#808080', '#800000',  '#808000', '#ffd8b1', '#000075',  '#ffffff', '#000000','#fffac8','#aaffc3')
celltype.colors<-my.distinct.colors[1:length(unique(my.data@meta.data$celltype))]
bloom.filepath<-"/home/yanxiting/Documents/Research/GRADS_SARC_PBMC/Data/analysis4b_BLOOM.tsv"
bloom.data<-as.data.frame(read_tsv(bloom.filepath))
Parsed with column specification:
cols(
  NAME = col_character(),
  SYMBOL = col_character(),
  TITLE = col_character(),
  `RANK IN GENE LIST` = col_double(),
  `RANK METRIC SCORE` = col_double(),
  `RUNNING ES` = col_double(),
  `CORE ENRICHMENT` = col_character(),
  X8 = col_logical()
)
bloom.genes<-bloom.data[bloom.data$`CORE ENRICHMENT`=="Yes","SYMBOL"]

temp.names<-intersect(bloom.genes,rownames(sc.data@assays$integrated@counts))

#output.filepath<-"~/driver_Grace/scratch/GRADS/SARC_results/Results_summary_PBMC_hg38/baseline/scRNA_compare/featureplot_bloom_4b.pdf"
#output.filepath<-"/home/yanxiting/Documents/Research/GRADS_SARC_PBMC/scRNA_compare/featureplot_bloom_4b.pdf"
#pdf(output.filepath,width = 8,height=8,onefile = T)
#g.list<-list()

for(i in 1:length(temp.names)){
#temp.data<-my.data@meta.data
#g.1<-ggplot(temp.data,aes(x=UMAP_1,y=UMAP_2,colour=celltype))+geom_point(size=0.1,shape=19)+scale_color_manual(values=celltype.colors)+guides(color = guide_legend(override.aes = list(size = 8)))

Idents(my.data)<-my.data@meta.data$celltype
g.1<-DimPlot(my.data, reduction = "umap",cols=celltype.colors,shuffle=T)
g.2<-FeaturePlot(my.data,features=temp.names[i],pt.size = 0.1,order=T)
g<-grid.arrange(g.1,g.2,ncol=2)

output.filepath<-file.path("/home/yanxiting/Documents/Research/GRADS_SARC_PBMC/scRNA_compare",paste("featureplot_bloom_4b_",temp.names[i],".pdf"))
ggsave(output.filepath,g,device="pdf",width=10,height=5)
}
The following functions and any applicable methods accept the dots: CombinePlots
The following functions and any applicable methods accept the dots: CombinePlots
The following functions and any applicable methods accept the dots: CombinePlots
The following functions and any applicable methods accept the dots: CombinePlots
The following functions and any applicable methods accept the dots: CombinePlots
The following functions and any applicable methods accept the dots: CombinePlots
The following functions and any applicable methods accept the dots: CombinePlots
The following functions and any applicable methods accept the dots: CombinePlots
The following functions and any applicable methods accept the dots: CombinePlots
The following functions and any applicable methods accept the dots: CombinePlots
The following functions and any applicable methods accept the dots: CombinePlots
The following functions and any applicable methods accept the dots: CombinePlots
The following functions and any applicable methods accept the dots: CombinePlots
The following functions and any applicable methods accept the dots: CombinePlots
The following functions and any applicable methods accept the dots: CombinePlots
The following functions and any applicable methods accept the dots: CombinePlots
The following functions and any applicable methods accept the dots: CombinePlots
The following functions and any applicable methods accept the dots: CombinePlots
The following functions and any applicable methods accept the dots: CombinePlots
The following functions and any applicable methods accept the dots: CombinePlots
The following functions and any applicable methods accept the dots: CombinePlots
The following functions and any applicable methods accept the dots: CombinePlots
The following functions and any applicable methods accept the dots: CombinePlots

Integrated Analysis

Conduct integrated analysis to remove donor effect. We have not succeeded in the integration analysis due to the very small number of cells per subject.

# split the dataset into a list of two seurat objects (stim and CTRL)
ifnb.list <- SplitObject(my.data, split.by = "donor")
temp.size<-matrix(unlist(lapply(ifnb.list,dim)),ncol=2,byrow=T)[,2]
#ifnb.list<-ifnb.list[temp.size>=30]

# normalize and identify variable features for each dataset independently
ifnb.list <- lapply(X = ifnb.list, FUN = function(x) {
    x <- NormalizeData(x)
    x <- FindVariableFeatures(x, selection.method = "vst", nfeatures = 2000)
})
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
# select features that are repeatedly variable across datasets for integration
features <- SelectIntegrationFeatures(object.list = ifnb.list)
ifnb.list <- lapply(X = ifnb.list, FUN = function(x) {
    x <- ScaleData(x, features = features, verbose = FALSE)
    x <- RunPCA(x, features = features, verbose = FALSE, approx=FALSE)
})
Error in pca.results$x %*% diag(pca.results$sdev[1:npcs]^2) : 
  non-conformable arguments
# left over
my.data@assays$integrated<-sc.data@assays$integrated
my.data@meta.data<-cbind(my.data@meta.data,mycol.data)

Idents(my.data)<-my.data@meta.data$celltype
# identify the cell type marker genes
#Idents(sc.data)<-sc.data@meta.data$celltype
celltype.names<-unique(as.character(my.data@meta.data$celltype))
sc.markers.result<-list()

for(i in 1:length(celltype.names)){
  sc.markers.result[[i]]<-FindMarkers(my.data,ident.1=celltype.names[i],min.pct=0.05)
}

# save the marker lists
#output.filepath<-file.path("~/driver_Grace/scratch/GRADS/SARC_results/Results_summary_PBMC_hg38/baseline/data/GSE132338_SingleCellExperiment_markerlist.RDS")
output.filepath<-"/home/yanxiting/Documents/Research/GRADS_SARC_PBMC/scRNA_compare/GSE132338_SingleCellExperiment_markerlist.RDS"
saveRDS(sc.markers.result,file=output.filepath,refhook = NULL)

Draw the UMAPs to show the expression of bloom genes in this 10X data.

#bloom.filepath<-"~/driver_Grace/scratch/GRADS/SARC_results/Results_summary_PBMC_hg38/baseline/scRNA_compare/analysis4b_BLOOM.tsv"
my.distinct.colors<-c('#e6194b' , '#3cb44b', '#ffe119', '#4363d8', '#f58231', '#911eb4', '#46f0f0', '#f032e6', '#bcf60c', '#fabebe', '#008080', '#e6beff', '#9a6324', '#808080', '#800000',  '#808000', '#ffd8b1', '#000075',  '#ffffff', '#000000','#fffac8','#aaffc3')
celltype.colors<-my.distinct.colors[1:length(unique(my.data@meta.data$celltype))]
bloom.filepath<-"/home/yanxiting/Documents/Research/GRADS_SARC_PBMC/Data/analysis4b_BLOOM.tsv"
bloom.data<-as.data.frame(read_tsv(bloom.filepath))
bloom.genes<-bloom.data[bloom.data$`CORE ENRICHMENT`=="Yes","SYMBOL"]

temp.names<-intersect(bloom.genes,rownames(sc.data@assays$integrated@counts))

#output.filepath<-"~/driver_Grace/scratch/GRADS/SARC_results/Results_summary_PBMC_hg38/baseline/scRNA_compare/featureplot_bloom_4b.pdf"
output.filepath<-"/home/yanxiting/Documents/Research/GRADS_SARC_PBMC/scRNA_compare/featureplot_bloom_4b.pdf"
pdf(output.filepath,width = 8,height=8,onefile = T)
#g.list<-list()

for(i in 1:length(temp.names)){
temp.data<-my.data@meta.data
g.1<-ggplot(temp.data,aes(x=UMAP_1,y=UMAP_2,colour=celltype))+geom_point(size=0.1,shape=19)+scale_color_manual(values=celltype.colors)+guides(color = guide_legend(override.aes = list(size = 8)))

temp.color<-sc.data@assays$integrated@counts[temp.names[i],]
temp.data<-cbind(sc.data@meta.data,temp.color)
colnames(temp.data)[ncol(temp.data)]<-"col"
g.2<-ggplot(temp.data,aes(x=UMAP_1,y=UMAP_2,colour=col))+geom_point(size=0.01,shape=19)+scale_color_gradient(low="grey",high="red")+ggtitle(temp.names[i])

#output.filepath<-file.path("~/driver_Grace/scratch/GRADS/SARC_results/Results_summary_PBMC_hg38/baseline/scRNA_compare",paste("featureplot_bloom_4b_",temp.names[i],".pdf"))
g<-grid.arrange(g.1,g.2,ncol=2)
g
output.filepath<-file.path("/home/yanxiting/Documents/Research/GRADS_SARC_PBMC/scRNA_compare",paste("featureplot_bloom_4b_",temp.names[i],".pdf"))
ggsave(output.filepath,g,device="pdf",width=8,height=8)

}
dev.off()

First, we evaluate the enrichment of BLOOM genes in the marker gene lists

LS0tCnRpdGxlOiAiR1NFQSBhbmFseXNpcyBvZiBTQVJDIGFzc29jaWF0ZWQgZ2VuZXMiCmF1dGhvcjogIlhpdGluZyBZYW4iCmRhdGU6ICIxMC8wMi8yMDE5IgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgZmlnX2NhcHRpb246IHllcwogICAgaGlnaGxpZ2h0OiB0YW5nbwogICAgbnVtYmVyX3NlY3Rpb25zOiBubwogICAgdGhlbWU6IHVuaXRlZAogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogNgogICAgdG9jX2Zsb2F0OiB5ZXMKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGV2YWw9VFJVRSxlY2hvID0gVFJVRSxjYWNoZT1UUlVFLHdhcm5pbmc9RkFMU0UsbWVzc2FnZSA9IEZBTFNFLHJlc3VsdHM9J2hvbGQnLGNhY2hlLmxhenkgPSBGQUxTRSkKa25pdHI6Om9wdHNfa25pdCRzZXQoZXZhbC5hZnRlciA9ICdmaWcuY2FwJyxkZXY9YygncG5nJywncG9zdHNjcmlwdCcpKQoKCmxpYnJhcnkoY2FwdGlvbmVyKQpsaWJyYXJ5KGNpcmNsaXplKQpsaWJyYXJ5KGNsdXN0ZXJQcm9maWxlcikKbGlicmFyeShjb3JycGxvdCkKbGlicmFyeShDb21wbGV4SGVhdG1hcCkKbGlicmFyeShkcGx5cikKbGlicmFyeShFbnNEYi5Ic2FwaWVucy52ODYpCmxpYnJhcnkoZ2RhdGEpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShnZ3B1YnIpCmxpYnJhcnkoZ2dyZXBlbCkKbGlicmFyeShncGxvdHMpCmxpYnJhcnkoZ3JpZCkKbGlicmFyeShncmlkRXh0cmEpCmxpYnJhcnkoZ3JpZEdyYXBoaWNzKQpsaWJyYXJ5KGthYmxlRXh0cmEpCmxpYnJhcnkoa25pdHIpCmxpYnJhcnkoTWF0cml4KQpsaWJyYXJ5KG9yZy5Icy5lZy5kYikKbGlicmFyeShwYXJhbGxlbCkKbGlicmFyeShyZXNoYXBlMikKbGlicmFyeShzY2FsZXMpCmxpYnJhcnkoU2V1cmF0KQpsaWJyYXJ5KFNpbmdsZVIpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHZpcmlkaXNMaXRlKQpsaWJyYXJ5KHhsc3gpCmxpYnJhcnkocmFuZG9tY29sb1IpCgpsaWJyYXJ5KGthYmxlRXh0cmEpCmxpYnJhcnkoZ2RhdGEpCmxpYnJhcnkoa25pdHIpCmxpYnJhcnkoY2FwdGlvbmVyKQpsaWJyYXJ5KG5sbWUpCmxpYnJhcnkocmdsKQpsaWJyYXJ5KGdwbG90cykKbGlicmFyeShXR0NOQSkKbGlicmFyeSh4bHN4KQpsaWJyYXJ5KHJhbmRvbWNvbG9SKQpsaWJyYXJ5KENvbXBsZXhIZWF0bWFwKQoKa25pdF9ob29rcyRzZXQod2ViZ2wgPSBob29rX3dlYmdsKQoKCnRhYmxlX251bXNfMSA8LSBjYXB0aW9uZXI6OmNhcHRpb25lcihwcmVmaXg9IlRhYmxlIixsZXZlbHM9MSkKZmlndXJlX251bXNfMTwtIGNhcHRpb25lcjo6Y2FwdGlvbmVyKHByZWZpeD0iRmlndXJlIixsZXZlbHM9MSkKCgp0YWJsZV9udW1zXzIgPC0gY2FwdGlvbmVyOjpjYXB0aW9uZXIocHJlZml4PSJUYWJsZSIsbGV2ZWxzPTIpCmZpZ3VyZV9udW1zXzI8LSBjYXB0aW9uZXI6OmNhcHRpb25lcihwcmVmaXg9IkZpZ3VyZSIsbGV2ZWxzPTIpCgp0YWJsZV9udW1zXzMgPC0gY2FwdGlvbmVyOjpjYXB0aW9uZXIocHJlZml4PSJUYWJsZSIsbGV2ZWxzPTMpCmZpZ3VyZV9udW1zXzMgPC0gY2FwdGlvbmVyOjpjYXB0aW9uZXIocHJlZml4PSJGaWd1cmUiLGxldmVscz0zKQoKaG9tZS5kaXI8LSIvaG9tZS95YW54aXRpbmcvZHJpdmVyX0dyYWNlIgojaG9tZS5kaXI8LSIvaG9tZS94eTQ4Igpzb3VyY2UocGFzdGUoaG9tZS5kaXIsIi9ScHJvZ3JhbS9teV9mdW5jdGlvbnMuUiIsc2VwPSIiKSkKb3V0cHV0LmRpcjwtZmlsZS5wYXRoKGhvbWUuZGlyLCJzY3JhdGNoL0dSQURTL1NBUkNfcmVzdWx0cy9SZXN1bHRzX3N1bW1hcnlfUEJNQ19oZzM4L2Jhc2VsaW5lIikKYGBgCgpJbiB0aGlzIG5vdGUsIHdlIGdlbmVyYXRlIHRoZSBpbnB1dCBmaWxlcyBmb3IgR1NFQSB0byBjb25kdWN0IGdlbmUgc2V0IGVucmljaG1lbnQgYW5hbHlzaXMgb2YgdGhlIGlkZW50aWZpZWQgZ2VuZXMgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGJldHdlZW4gZ2l2ZW4gZ3JvdXBzIG9mIHBhdGllbnRzLgoKIyBUUE0gYWRqdXN0ZWQgZGF0YQoKV2UgZ2VuZXJhdGUgaW5wdXQgZmlsZXMgZm9yIHRoZSBkaWZmZXJlbnRpYWwgZXhwcmVzc2VkIGdlbmVzIGJldHdlZW4gbm9uYWN1dGUgc3RhZ2UgSSB1bnRyZWF0ZWQgdnMgYWxsIG90aGVyIGdyb3VwcyBiYXNlZCBvbiBMYXVyYSdzIG9ic2VydmF0aW9ucy4gCgpgYGB7cn0KCiMgb3V0cHV0IHRoZSBtZXJnZWQgY2xpbmljYWwgZGF0YSBtYXRyaXggYXMgYSB0eHQgZmlsZQpjbGluaWMuZmlsZXBhdGg8LSIvaG9tZS95YW54aXRpbmcvZHJpdmVyX0dyYWNlL3NjcmF0Y2gvR1JBRFMvU0FSQ19yZXN1bHRzL1Jlc3VsdHNfc3VtbWFyeV9QQk1DX2hnMzgvYmFzZWxpbmUvZGF0YS9jbGluaWNfbWF0cml4X21lcmdlZC5SRFMiCmNsaW5pYy5tYXRyaXg8LXJlYWRSRFMoY2xpbmljLmZpbGVwYXRoLHJlZmhvb2sgPSBOVUxMKQpvdXRwdXQuZmlsZXBhdGg8LSIvaG9tZS95YW54aXRpbmcvZHJpdmVyX0dyYWNlL3NjcmF0Y2gvR1JBRFMvU0FSQ19yZXN1bHRzL1Jlc3VsdHNfc3VtbWFyeV9QQk1DX2hnMzgvYmFzZWxpbmUvZGF0YS9jbGluaWNfbWF0cml4X21lcmdlZC50eHQiCndyaXRlLnRhYmxlKGNsaW5pYy5tYXRyaXgsZmlsZT1vdXRwdXQuZmlsZXBhdGgsc2VwPSJcdCIscm93Lm5hbWVzPUYsY29sLm5hbWVzPVQscXVvdGU9RixhcHBlbmQ9RikKCgoKbXlfZ3NlYV9maWxlY3JlYXRlX2JpbmFyeTwtZnVuY3Rpb24oZnBrbS5maWxlcGF0aCxjbGluaWMuZmlsZXBhdGgsdmFyLm5hbWVzLGdyb3VwMS52YWx1ZXMsZ3JvdXAyLnZhbHVlcyxncm91cDEubmFtZT0iZ3JvdXAxIixncm91cDIubmFtZT0iZ3JvdXAyIixvdXRwdXQuZGlyLHN1ZmZpeC5uYW1lKXsKICAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwogICMgQXJndW1lbnRzOgogICMgCiAgIyBnZXhwLmZpbGVwYXRoIGlzIHRoZSBmaWxlIHBhdGggb2YgdGhlIFRQTSBtYXRyaXggd2l0aCBmaXJzdCA1IGNvbHVtbnMgYmVpbmcgYW5ub3RhdGlvbiBmb3IgZ2VuZXMgCiAgIyAoVFBNX2Jhc2VsaW5lXzI3Nl9jbGVhbl9jZWxsZGlmZmFkanVzdGVkX3dpdGhhbm5vdC50eHQpLgogICMgCiAgIyBjbGluaWMuZmlscGF0aCBpcyB0aGUgZmlsZSBwYXRoIG9mIHRoZSBjbGluaWMgZGF0YSAoY2xpbmljX21hdHJpeF9tZXJnZWQudHh0KS4gVGhlIGZpcnN0IHJvdyBuZWVkcyB0byBiZSB0aGUgY29sdW1uIG5hbWVzLgogICMgCiAgIyB2YXIubmFtZXMgaXMgYSB2ZWN0b3IgY29udGFpbmluZyB0aGUgbmFtZXMgb2YgY29sdW1ucyBpbiBjbGluaWMuZmlsZXBhdGggdGhhdCB5b3Ugd2FudCB0byBnZW5lcmF0ZSB0aGUgcGhlbm90eXBlIGZpbGUgZm9yLgogICMKICAjIGdyb3VwMS52YWx1ZXMgaXMgYSBkYXRhLmZyYW1lIG9mIHN0cmluZyBkZXNjcmliaW5nIHZhbHVlcyBvZiB2YXIubmFtZXMgdGhhdCBhcmUgY29uc2lkZXJlZCBhcyBnb3J1cCAxLiAKICAjIEZvciBleGFtcGxlLCBncm91cDEudmFsdWVzPWRhdGEuZnJhbWUoUEhFTkdSUD0ibm9uYWN1dGUgU3RhZ2UgSSB1bnRyZWF0ZWQiKS4gTm90ZSB0aGF0IHRoZSBjb2x1bW5zIGluIGdyb3VwMS52YWx1ZXMgbmVlZCB0byBtYXRjaCB0aG9zZSBpbiB2YXIubmFtZXMgaWYgdGhlcmUgYXJlCiAgIyBtb3JlIHRoYW4gMSB2YXJpYWJsZSB0byBkZWZpbmUgdGhlIGdyb3Vwcy4KICAjCiAgIyBncm91cDIudmFsdWVzIGhhcyB0aGUgc2FtZSBmb3JtYXQgYXMgZ3JvdXAxLnZhbHVlcyBidXQgaXMgdGhlIHNldHRpbmdzIGZvciBncm91cDIuCiAgIwogICMgZ3JvdXAxLm5hbWUgaXMgdGhlIG5hbWUgeW91IHdhbnQgdG8gY2FsbCB5b3VyIGdyb3VwMSBpbiB0aGUgY2xzIGZpbGUuCiAgIwogICMgZ3JvdXAyLm5hbWUgaXMgdGhlIG5hbWUgeW91IHdhbnQgdG8gY2FsbCB5b3VyIGdyb3VwMiBpbiB0aGUgY2xzIGZpbGUuCiAgIyAKICAjIG91dHB1dC5kaXIgaXMgdGhlIGZvbGRlciBuYW1lIHdoZXJlIHlvdSB3YW50IHRvIHNhdmUgdGhlIGNyZWF0ZWQgZmlsZXMuCiAgIwogICMgc3VmZml4Lm5hbWUgaXMgdGhlIHN1ZmZpeCBuYW1lIGluIHRoZSBuYW1lIG9mIHRoZSBvdXRwdXQgZmlsZXMuIFRoZSBnY3QgZmlsZSB3aWxsIGJlIG5hbWVkIGFzIGdjdF9zdWZmaXgubmFtZS5nY3QgYW5kIGNscyBmaWxlIHdpbGwgYmUgbmFtZWQgYXMKICAjIGNsc19zdWZmaXgubmFtZS5nY3QuIEZvciBleGFtcGxlLCBpZiBzdWZmaXgubmFtZT0idGVzdCIsIHRoZSBnY3QgZmlsZSB3aWxsIGJlIG5hbWVkICJnY3RfdGVzdC5nY3QiIGFuZCB0aGUgY2xzIGZpbGUgd2lsbCBiZSBuYW1lZCBhcyAKICAjICJjbHNfdGVzdC5jbHMiLgogICMgCiAgIyBWYWx1ZToKICAjCiAgIyBUaGlzIGZ1bmN0aW9uIHdpbGwgcmV0dXJuIDAgaWYgc3VjY2Vzc2Z1bGx5IHJhbi4gT3RoZXJ3aXNlLCBpdCB3aWxsIHJldHVybiAxLiBJZiB1bnN1Y2Nlc3NmdWwsIGluZm9ybWF0aW9uIHJlZ2FyZGluZyB3aGF0IHdlbnQgd3Jvbmcgd2lsbCBiZSBzcGl0IAogICMgb3V0IGFzIHdhcm5pbmcgbWVzc2FnZXMuCiAgIwogICMgV2hlbiByYW4gc3VjY2Vzc2Z1bGx5LCB0aGlzIGZ1bmN0aW9uIHdpbGwgY3JlYXRlIHR3byBmaWxlcyB1bmRlciB0aGUgc3BlY2lmaWVkIG91dHB1dC5kaXIgbmFtZWQgZ2N0X3Zhci5uYW1lLmdjdCBhbmQgY2xzX3Zhci5uYW1lLmNscywgd2hlcmUgCiAgIyB2YXIubmFtZSB3aWxsIGJlIHRoZSBzcGVmaWNpZWQgdmFsdWUgCiAgIyBpbiB0aGUgYXJndW1lbnQuIFRoZXNlIHR3byBmaWxlcyBjYW4gYmUgZGlyZWN0bHkgbG9hZGVkIGludG8gR1NFQSB0b2dldGhlciB3aXRoIGFub3RoZXIgZ2VuZSBzZXQgZmlsZSB0aGF0IG5lZWRzIHRvIGJlIGdlbmVyYXRlZCBvdXRzaWRlIG9mIHRoaXMgCiAgIyBmdW5jdGlvbi4KICAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwogIAogICMgIGdlbmVyYXRlIHRoZSBnZW5lIGV4cHJlc3Npb24gbWF0cml4IGlucHV0IGZpbGUgZm9yIEdTRUEKICAjZnBrbS5maWxlcGF0aDwtZmlsZS5wYXRoKGhvbWUuZGlyLCJzY3JhdGNoL0dSQURTL1NBUkNfcmVzdWx0cy9SZXN1bHRzX3N1bW1hcnlfUEJNQ19oZzM4L2Jhc2VsaW5lL2RhdGFfYWRqdXN0ZWQyIiwiVFBNX2Jhc2VsaW5lXzI3Nl9jbGVhbl9jZWxsZGlmZmFkanVzdGVkX3dpdGhhbm5vdC50eHQiKQogICNjbGluaWMuZmlsZXBhdGg8LSIvaG9tZS95YW54aXRpbmcvZHJpdmVyX0dyYWNlL3NjcmF0Y2gvR1JBRFMvU0FSQ19yZXN1bHRzL1Jlc3VsdHNfc3VtbWFyeV9QQk1DX2hnMzgvYmFzZWxpbmUvZGF0YS9jbGluaWNfbWF0cml4X21lcmdlZC50eHQiCiAgI291dHB1dC5kaXI8LSIvaG9tZS95YW54aXRpbmcvZHJpdmVyX0dyYWNlL3NjcmF0Y2gvR1JBRFMvU0FSQ19yZXN1bHRzL1Jlc3VsdHNfc3VtbWFyeV9QQk1DX2hnMzgvYmFzZWxpbmUvR1NFQV9rbm93bmdlbmVzIgogICMgc3VmZml4Lm5hbWU8LSJhbmFseXNpczEiCiAgCiAgCiAgIyBsb2FkIGluIHRoZSBmcGttIG1hdHJpeAogIGZwa20ubWF0cml4PC1yZWFkLnRhYmxlKGZwa20uZmlsZXBhdGgsc2VwPSJcdCIsaGVhZGVyPVQsY2hlY2submFtZXM9Rixhcy5pcz1UUlVFLGNvbW1lbnQuY2hhciA9ICIiKQogIGZwa20ubWF0cml4LmFubm88LWZwa20ubWF0cml4WywxOjVdCiAgZnBrbS5tYXRyaXg8LWZwa20ubWF0cml4Wyw2Om5jb2woZnBrbS5tYXRyaXgpXQogIAogICMgbG9hZCBpbiB0aGUgY2xpbmljYWwgZGF0YSBhbmQgc3Vic2V0IHRoZSBzYW1wbGVzIHRvIHRob3NlIGxpc3RlZCBpbiB0aGUgZnBrbS5tYXRyaXgKICBjbGluaWMubWF0cml4PXJlYWQudGFibGUoY2xpbmljLmZpbGVwYXRoLHNlcD0iXHQiLGhlYWRlcj1ULGNoZWNrLm5hbWVzPUYsc3RyaW5nc0FzRmFjdG9ycz1GKQogIHJvd25hbWVzKGNsaW5pYy5tYXRyaXgpPC1hcy5tYXRyaXgoY2xpbmljLm1hdHJpeClbLDFdICMgR1JBRFMgSUQKICBjbGluaWMubWF0cml4PC1jbGluaWMubWF0cml4W2NvbG5hbWVzKGZwa20ubWF0cml4KSxdCgogICMgYmFzZWQgb24gdmFyLm5hbWUsIGdyb3VwMS52YWx1ZXMsIGFuZCBncm91cDIudmFsdWVzLCBpZGVudGlmeSB0aGUgc2FtcGxlcyB0byBpbmNsdWRlIGluIHRoZSBmaWxlcy4KICBpZihmaWxlLmV4aXN0cyhvdXRwdXQuZGlyKT09Ril7CiAgICBkaXIuY3JlYXRlKG91dHB1dC5kaXIpCiAgfQogIAogIGdjdC5maWxlcGF0aDwtZmlsZS5wYXRoKG91dHB1dC5kaXIscGFzdGUoImdleHBfIixzdWZmaXgubmFtZSwiLmdjdCIsc2VwPSIiKSkKICBjbHMuZmlsZXBhdGg8LWZpbGUucGF0aChvdXRwdXQuZGlyLHBhc3RlKCJjbHNfIixzdWZmaXgubmFtZSwiLmNscyIsc2VwPSIiKSkKICAKICAjIHN1YnN0cmFjdCBzYW1wbGVzIHdpdGggdmFyLm5hbWUgZXF1YWwgdG8gdGhlIHZhbHVlcyBpbiBncm91cDEudmFsdWVzIGFuZCBncm91cDIuIHZhbHVlcwogIHRlbXAxPC1mcGttLm1hdHJpeFssYXBwbHkoYXMuZGF0YS5mcmFtZShjbGluaWMubWF0cml4Wyx2YXIubmFtZXNdKSwxLHBhc3RlLGNvbGxhcHNlPSJfIiklaW4lYXBwbHkoZ3JvdXAxLnZhbHVlcywxLHBhc3RlLGNvbGxhcHNlPSJfIildCiAgdGVtcDI8LWZwa20ubWF0cml4WyxhcHBseShhcy5kYXRhLmZyYW1lKGNsaW5pYy5tYXRyaXhbLHZhci5uYW1lc10pLDEscGFzdGUsY29sbGFwc2U9Il8iKSVpbiVhcHBseShncm91cDIudmFsdWVzLDEscGFzdGUsY29sbGFwc2U9Il8iKV0KICBjYXQoIlRoZXJlIGFyZSAiLG5jb2wodGVtcDEpLCIgYW5kICIsIG5jb2wodGVtcDIpLCIgc2FtcGxlcyBmb3IgZ3JvdXAxIGFuZCBncm91cDIsIHJlc3BlY3RpdmVseS5cbiIpCiAgZGF0YS5tYXRyaXg8LWNiaW5kKHRlbXAxLHRlbXAyKQogIGRhdGEubWF0cml4PC1jYmluZChmcGttLm1hdHJpeC5hbm5vWywxXSxyZXAoIm5hIixucm93KGRhdGEubWF0cml4KSksZGF0YS5tYXRyaXgpCiAgY29sbmFtZXMoZGF0YS5tYXRyaXgpWzE6Ml08LWMoIk5BTUUiLCJEZXNjcmlwdGlvbiIpCiAgcGhlbm8udmVjdDwtYyhyZXAoMCxuY29sKHRlbXAxKSkscmVwKDEsbmNvbCh0ZW1wMikpKQogIAogICMgb3V0cHV0IHRoZSBnZW5lIGV4cHJlc3Npb24gZGF0YQogIGNtZC5vdXQ8LSIjMS4yXG4iCiAgY21kLm91dDwtcGFzdGUoY21kLm91dCxucm93KGRhdGEubWF0cml4KSwiXHQiLG5jb2woZGF0YS5tYXRyaXgpLTIsIlxuIixzZXA9IiIpCiAgY2F0KGNtZC5vdXQsZmlsZT1nY3QuZmlsZXBhdGgsYXBwZW5kPUYpCiAgd3JpdGUudGFibGUoZGF0YS5tYXRyaXgsZmlsZT1nY3QuZmlsZXBhdGgsYXBwZW5kPVQscm93Lm5hbWVzPUYsY29sLm5hbWVzPVQscXVvdGU9RikKICAKICAjIG91dHB1dCB0aGUgcGhlbm90eXBlIGZpbGUKICBjbWQub3V0PC1wYXN0ZShuY29sKGRhdGEubWF0cml4KS0yLCJcdCIsMiwiXHQiLDEsIlxuIixzZXA9IiIpCiAgY21kLm91dDwtcGFzdGUoY21kLm91dCwiIyBncm91cDEgZ3JvdXAyXG4iLHNlcD0iIikKICBjYXQoY21kLm91dCxmaWxlPWNscy5maWxlcGF0aCxhcHBlbmQ9RixzZXA9IiIpCiAgY2F0KHBoZW5vLnZlY3QsZmlsZT1jbHMuZmlsZXBhdGgsYXBwZW5kPVQsc2VwPSIgIikKICAKICByZXR1cm4oMCkKfQoKCgpmcGttLmZpbGU9ZmlsZS5wYXRoKGhvbWUuZGlyLCJzY3JhdGNoL0dSQURTL1NBUkNfcmVzdWx0cy9SZXN1bHRzX3N1bW1hcnlfUEJNQ19oZzM4L2Jhc2VsaW5lL2RhdGFfYWRqdXN0ZWQyIiwiVFBNX2Jhc2VsaW5lXzI3Nl9jbGVhbl9jZWxsZGlmZmFkanVzdGVkX3dpdGhhbm5vdC50eHQiKQpjbGluaWMuZmlsZTwtIi9ob21lL3lhbnhpdGluZy9kcml2ZXJfR3JhY2Uvc2NyYXRjaC9HUkFEUy9TQVJDX3Jlc3VsdHMvUmVzdWx0c19zdW1tYXJ5X1BCTUNfaGczOC9iYXNlbGluZS9kYXRhL2NsaW5pY19tYXRyaXhfbWVyZ2VkLnR4dCIKb3V0cHV0LmZvbGRlcjwtIi9ob21lL3lhbnhpdGluZy9kcml2ZXJfR3JhY2Uvc2NyYXRjaC9HUkFEUy9TQVJDX3Jlc3VsdHMvUmVzdWx0c19zdW1tYXJ5X1BCTUNfaGczOC9iYXNlbGluZS9HU0VBX2tub3duZ2VuZXMiCgojIGdlbmVyYXRlIHRoZSBmaWxlcyBmb3IgYW5hbHlzaXMgMQp2YXJuYW1lczwtYygiUEhFTkdSUCIpCm15X2dzZWFfZmlsZWNyZWF0ZV9iaW5hcnkoZnBrbS5maWxlcGF0aD1mcGttLmZpbGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgY2xpbmljLmZpbGVwYXRoID0gY2xpbmljLmZpbGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyLm5hbWVzID0gdmFybmFtZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAxLnZhbHVlcyA9IGRhdGEuZnJhbWUoUEhFTkdSUD1jKCJOb24tYWN1dGUsIFN0YWdlIEksIHVudHJlYXRlZCIpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cDIudmFsdWVzID0gZGF0YS5mcmFtZShQSEVOR1JQPWMoIlN0YWdlIElJLUlJSSwgdW50cmVhdGVkIiwiU3RhZ2UgSVYsIHVudHJlYXRlZCIpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBvdXRwdXQuZGlyPW91dHB1dC5mb2xkZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgc3VmZml4Lm5hbWU9ImFuYWx5c2lzMSIKICAgICAgICAgICAgICAgICAgICAgICAgICApCgojIGdlbmVyYXRlIHRoZSBmaWxlcyBmb3IgYW5hbHlzaXMgMgp2YXJuYW1lczwtYygiUEhFTkdSUCIpCm15X2dzZWFfZmlsZWNyZWF0ZV9iaW5hcnkoZnBrbS5maWxlcGF0aD1mcGttLmZpbGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgY2xpbmljLmZpbGVwYXRoID0gY2xpbmljLmZpbGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyLm5hbWVzID0gdmFybmFtZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAxLnZhbHVlcyA9IGRhdGEuZnJhbWUoUEhFTkdSUD1jKCJSZW1pdHRpbmcsIHVudHJlYXRlZCIpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cDIudmFsdWVzID0gZGF0YS5mcmFtZShQSEVOR1JQPWMoIk5vbi1hY3V0ZSwgU3RhZ2UgSSwgdW50cmVhdGVkIiwiU3RhZ2UgSUktSUlJLCB1bnRyZWF0ZWQiLCJTdGFnZSBJViwgdW50cmVhdGVkIikpLAogICAgICAgICAgICAgICAgICAgICAgICAgIG91dHB1dC5kaXI9b3V0cHV0LmZvbGRlciwKICAgICAgICAgICAgICAgICAgICAgICAgICBzdWZmaXgubmFtZT0iYW5hbHlzaXMyIgogICAgICAgICAgICAgICAgICAgICAgICAgICkKCiMgZ2VuZXJhdGUgdGhlIGZpbGVzIGZvciBhbmFseXNpcyAzCnZhcm5hbWVzPC1jKCJQSEVOR1JQIikKbXlfZ3NlYV9maWxlY3JlYXRlX2JpbmFyeShmcGttLmZpbGVwYXRoPWZwa20uZmlsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICBjbGluaWMuZmlsZXBhdGggPSBjbGluaWMuZmlsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICB2YXIubmFtZXMgPSB2YXJuYW1lcywKICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cDEudmFsdWVzID0gZGF0YS5mcmFtZShQSEVOR1JQPWMoIlN0YWdlIElWLCB1bnRyZWF0ZWQiKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAyLnZhbHVlcyA9IGRhdGEuZnJhbWUoUEhFTkdSUD1jKCJOb24tYWN1dGUsIFN0YWdlIEksIHVudHJlYXRlZCIsIlN0YWdlIElJLUlJSSwgdW50cmVhdGVkIikpLAogICAgICAgICAgICAgICAgICAgICAgICAgIG91dHB1dC5kaXI9b3V0cHV0LmZvbGRlciwKICAgICAgICAgICAgICAgICAgICAgICAgICBzdWZmaXgubmFtZT0iYW5hbHlzaXMzIgogICAgICAgICAgICAgICAgICAgICAgICAgICkKIyBnZW5lcmF0ZSB0aGUgZmlsZXMgZm9yIGFuYWx5c2lzIDQuIElmIHdlIGRpcmVjdGx5IHNwZWNpZnkgZ3JvdXAxLnZhbHVlcyB1c2luZyBudW1iZXJzLCBpdCB3b24ndCB3b3JrLiBJIGhhZCB0byBzcGVjaWZ5IHRoZW0gdG8gYmUgYSBzcGFjZStudW1iZXIgYXMgYSBjaGFyYWN0ZXIuCnZhcm5hbWVzPC1jKCJub2R1bGVfbHltcGhfcGhlbm8iLCJzdGVyb2lkX2F0djEiLCJkbWFyZF9hdHYxIikKbXlfZ3NlYV9maWxlY3JlYXRlX2JpbmFyeShmcGttLmZpbGVwYXRoPWZwa20uZmlsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICBjbGluaWMuZmlsZXBhdGggPSBjbGluaWMuZmlsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICB2YXIubmFtZXMgPSB2YXJuYW1lcywKICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cDEudmFsdWVzID0gZGF0YS5mcmFtZShub2R1bGVfbHltcGhfcGhlbm89YygibHltcGgiKSxzdGVyb2lkX2F0djE9YygiIDAiKSxkbWFyZF9hdHYxPWMoIiAwIikpLAogICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwMi52YWx1ZXMgPSBkYXRhLmZyYW1lKG5vZHVsZV9seW1waF9waGVubz1jKCJtaWNyb25vZHVsZSIsImJvdGgiKSxzdGVyb2lkX2F0djE9YygiIDAiLCIgMCIpLGRtYXJkX2F0djE9YygiIDAiLCIgMCIpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBvdXRwdXQuZGlyPW91dHB1dC5mb2xkZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgc3VmZml4Lm5hbWU9ImFuYWx5c2lzNCIKICAgICAgICAgICAgICAgICAgICAgICAgICApCmBgYAoKCgojIERFU2VxMiBsb2cyIGFkanVzdGVkIGRhdGEKCiMjIEdTRUEgaW5wdXQgZmlsZXMgZ2VuZXJhdGlvbgpXZSBmaXJzdCBnZW5lcmF0ZSBmaWxlIGNvbnRhaW5pbmcgYm90aCB0aGUgY2xpbmljYWwgbWF0cml4IGFuZCBDVCBzY2FuIGZlYXR1cmVzIHNvIHRoYXQgYm90aCBncm91cCAxIGFuZCBncm91cCAyIGluIHRoZSBHU0VBIGFuYWx5c2lzIGNhbiBiZSBkZWZpbmVkIGJhc2VkIG9uIGNvbWJpbmF0aW9ucyBvZiBjbGluaWNhbCB0cmlhdHMgYW5kIENUIHNjYW4gZGF0YS4KCmBgYHtyIGV2YWw9RkFMU0V9CiMgVGhlc2UgY29kZXMgb25seSBuZWVkIHRvIGJlIHJ1biBvbmNlCiMgbG9hZCBpbiB0aGUgY2xpbmljYWwgbWF0cml4CmNsaW5pYy5maWxlcGF0aDwtIi9ob21lL3lhbnhpdGluZy9kcml2ZXJfR3JhY2Uvc2NyYXRjaC9HUkFEUy9TQVJDX3Jlc3VsdHMvUmVzdWx0c19zdW1tYXJ5X1BCTUNfaGczOC9iYXNlbGluZS9kYXRhL2NsaW5pY19tYXRyaXhfbWVyZ2VkLlJEUyIKY2xpbmljLm1hdHJpeDwtcmVhZFJEUyhjbGluaWMuZmlsZXBhdGgscmVmaG9vayA9IE5VTEwpCiMgbG9hZCBpbiB0aGUgQ1Qgc2NhbiBkYXRhCmN0LmZpbGVwYXRoPC1maWxlLnBhdGgoaG9tZS5kaXIsInNjcmF0Y2gvR1JBRFMvU0FSQ19yZXN1bHRzL0RhdGEvQ1RfZGF0YV8yMDE3MDcxMi9TYXJjX2N0X3JlYWRzX2NvcnJlY3RlZC54bHMiKQpjdC5tYXRyaXg8LXJlYWQueGxzKGN0LmZpbGVwYXRoLHNoZWV0PTEpCnJvd25hbWVzKGN0Lm1hdHJpeCk8LWFzLm1hdHJpeChjdC5tYXRyaXgpWywiR1JBRFNJRCJdCmN0Lm1hdHJpeDwtY3QubWF0cml4WyxjKCJNZWRfTHltcGhhZGVub3BhdGh5IiwiSGlsYXJfTHltcGhhZGVub3BhdGh5IiwiTWljcm9ub2R1bGUiLCJCcm9uY2hpYWxfV2FsbF9UaGlja2VuaW5nIiwiVHJhY3Rpb25fQnJvbmNoaWVjdGFzaXMiLCJCcm9uY2hpZWN0YXNpc19zZXZlcml0eSIsIkdyb3VuZF9HbGFzcyIsIkhvbmV5Y29tYmluZyIsIlJldGljdWxhcl9BYm5vcm1hbGl0eSIsIlB1bG1vbmFyeV9BcnQiLCJUcmVlX2luX2J1ZCIpXQoKIyBsb2FkIGluIHRoZSBmcGttIG1hdHJpeApmcGttLmZpbGU9ZmlsZS5wYXRoKGhvbWUuZGlyLCJzY3JhdGNoL0dSQURTL1NBUkNfcmVzdWx0cy9SZXN1bHRzX3N1bW1hcnlfUEJNQ19oZzM4L2Jhc2VsaW5lL2RhdGFfYWRqdXN0ZWQyIiwiREVTZXEyX25vcm1hbGl6ZWRfMjc2X2NsZWFuX2xvZzJfY2VsbGRpZmZhZGp1c3RlZF93aXRoYW5ub3QudHh0IikKCmZwa20ubWF0cml4PC1yZWFkLnRhYmxlKGZwa20uZmlsZSxzZXA9Ilx0IixoZWFkZXI9VCxjaGVjay5uYW1lcz1GLGFzLmlzPVRSVUUsY29tbWVudC5jaGFyID0gIiIpCgpmcGttLm1hdHJpeC5hbm5vPC1mcGttLm1hdHJpeFssMTo2XQpmcGttLm1hdHJpeDwtZnBrbS5tYXRyaXhbLDc6bmNvbChmcGttLm1hdHJpeCldCgojIG1lcmdlIHRoZSB0d28gZGF0YQpjbGluaWMubWF0cml4PC1jbGluaWMubWF0cml4W2NvbG5hbWVzKGZwa20ubWF0cml4KSxdCmN0Lm1hdHJpeDwtY3QubWF0cml4W2NvbG5hbWVzKGZwa20ubWF0cml4KSxdCnJvd25hbWVzKGNsaW5pYy5tYXRyaXgpPC1jb2xuYW1lcyhmcGttLm1hdHJpeCkKcm93bmFtZXMoY3QubWF0cml4KTwtY29sbmFtZXMoZnBrbS5tYXRyaXgpCgpjbWQub3V0PC1jYmluZChjbGluaWMubWF0cml4LGN0Lm1hdHJpeCkKb3V0cHV0LmZpbGVwYXRoPC1maWxlLnBhdGgoaG9tZS5kaXIsInNjcmF0Y2gvR1JBRFMvU0FSQ19yZXN1bHRzL1Jlc3VsdHNfc3VtbWFyeV9QQk1DX2hnMzgvYmFzZWxpbmUvZGF0YS9jbGluaWNfY3RfbWVyZ2VfbWF0cml4X3dpdGhnZXguUkRTIikKc2F2ZVJEUyhjbWQub3V0LGZpbGU9b3V0cHV0LmZpbGVwYXRoLHJlZmhvb2sgPSBOVUxMKQoKb3V0cHV0LmZpbGVwYXRoPC1maWxlLnBhdGgoaG9tZS5kaXIsInNjcmF0Y2gvR1JBRFMvU0FSQ19yZXN1bHRzL1Jlc3VsdHNfc3VtbWFyeV9QQk1DX2hnMzgvYmFzZWxpbmUvZGF0YS9jbGluaWNfY3RfbWVyZ2VfbWF0cml4X3dpdGhnZXgudHh0IikKd3JpdGUudGFibGUoY21kLm91dCxmaWxlPW91dHB1dC5maWxlcGF0aCxzZXA9Ilx0Iixyb3cubmFtZXM9Rixjb2wubmFtZXM9VCxxdW90ZT1GLGFwcGVuZD1GKQoKIyBjaGFuZ2UgdGhlIGZpbGUgYWNjZXNzIHJpZ2h0IHNvIHRoYXQgdGhlc2UgdHdvIGZpbGVzIHdvbid0IGJlIGNoYW5nZWQgYWNjaWRlbnRhbGx5Cm91dHB1dC5maWxlcGF0aDwtZmlsZS5wYXRoKGhvbWUuZGlyLCJzY3JhdGNoL0dSQURTL1NBUkNfcmVzdWx0cy9SZXN1bHRzX3N1bW1hcnlfUEJNQ19oZzM4L2Jhc2VsaW5lL2RhdGEvY2xpbmljX2N0X21lcmdlX21hdHJpeF93aXRoZ2V4LlJEUyIpCnN5c3RlbShwYXN0ZSgiY2htb2QgYS13ICIsb3V0cHV0LmZpbGVwYXRoLHNlcD0iIikpCgpvdXRwdXQuZmlsZXBhdGg8LWZpbGUucGF0aChob21lLmRpciwic2NyYXRjaC9HUkFEUy9TQVJDX3Jlc3VsdHMvUmVzdWx0c19zdW1tYXJ5X1BCTUNfaGczOC9iYXNlbGluZS9kYXRhL2NsaW5pY19jdF9tZXJnZV9tYXRyaXhfd2l0aGdleC50eHQiKQpzeXN0ZW0ocGFzdGUoImNobW9kIGEtdyAiLG91dHB1dC5maWxlcGF0aCxzZXA9IiIpKQoKYGBgCgpBbHNvIGdlbmVyYXRlIHRoZSBjbGluaWNhbCBtYXRyaXggZmlsZS4KYGBge3IgZXZhbD1GQUxTRX0KIyBUaGVzZSBjb2RlcyBvbmx5IG5lZWQgdG8gYmUgcnVuIG9uY2UuCmNsaW5pYy5maWxlcGF0aDwtIi9ob21lL3lhbnhpdGluZy9kcml2ZXJfR3JhY2Uvc2NyYXRjaC9HUkFEUy9TQVJDX3Jlc3VsdHMvUmVzdWx0c19zdW1tYXJ5X1BCTUNfaGczOC9iYXNlbGluZS9kYXRhL2NsaW5pY19tYXRyaXhfbWVyZ2VkLlJEUyIKY2xpbmljLm1hdHJpeDwtcmVhZFJEUyhjbGluaWMuZmlsZXBhdGgscmVmaG9vayA9IE5VTEwpCm91dHB1dC5maWxlcGF0aDwtIi9ob21lL3lhbnhpdGluZy9kcml2ZXJfR3JhY2Uvc2NyYXRjaC9HUkFEUy9TQVJDX3Jlc3VsdHMvUmVzdWx0c19zdW1tYXJ5X1BCTUNfaGczOC9iYXNlbGluZS9kYXRhL2NsaW5pY19tYXRyaXhfbWVyZ2VkLnR4dCIKd3JpdGUudGFibGUoY2xpbmljLm1hdHJpeCxmaWxlPW91dHB1dC5maWxlcGF0aCxzZXA9Ilx0Iixyb3cubmFtZXM9Rixjb2wubmFtZXM9VCxxdW90ZT1GLGFwcGVuZD1GKQoKYGBgCgpXZSBnZW5lcmF0ZSBpbnB1dCBmaWxlcyBmb3IgdGhlIGRpZmZlcmVudGlhbCBleHByZXNzZWQgZ2VuZXMgYmV0d2VlbiBub25hY3V0ZSBzdGFnZSBJIHVudHJlYXRlZCB2cyBhbGwgb3RoZXIgZ3JvdXBzIGJhc2VkIG9uIExhdXJhJ3Mgb2JzZXJ2YXRpb25zLiAKCmBgYHtyfQoKIyBvdXRwdXQgdGhlIG1lcmdlZCBjbGluaWNhbCBkYXRhIG1hdHJpeCBhcyBhIHR4dCBmaWxlCmNsaW5pYy5maWxlcGF0aDwtIi9ob21lL3lhbnhpdGluZy9kcml2ZXJfR3JhY2Uvc2NyYXRjaC9HUkFEUy9TQVJDX3Jlc3VsdHMvUmVzdWx0c19zdW1tYXJ5X1BCTUNfaGczOC9iYXNlbGluZS9kYXRhL2NsaW5pY19tYXRyaXhfbWVyZ2VkLlJEUyIKY2xpbmljLm1hdHJpeDwtcmVhZFJEUyhjbGluaWMuZmlsZXBhdGgscmVmaG9vayA9IE5VTEwpCiNvdXRwdXQuZmlsZXBhdGg8LSIvaG9tZS95YW54aXRpbmcvZHJpdmVyX0dyYWNlL3NjcmF0Y2gvR1JBRFMvU0FSQ19yZXN1bHRzL1Jlc3VsdHNfc3VtbWFyeV9QQk1DX2hnMzgvYmFzZWxpbmUvZGF0YS9jbGluaWNfbWF0cml4X21lcmdlZC50eHQiCiN3cml0ZS50YWJsZShjbGluaWMubWF0cml4LGZpbGU9b3V0cHV0LmZpbGVwYXRoLHNlcD0iXHQiLHJvdy5uYW1lcz1GLGNvbC5uYW1lcz1ULHF1b3RlPUYsYXBwZW5kPUYpCgoKIyBub3RlIHRoaXMgZnVuY3Rpb24gaXMgc2xpZ2h0bHkgZGlmZmVyZW50IGZvciBERVNlcTIgTm9ybSBkYXRhIGFuZCB0aGUgVFBNIG1hdHJpeApteV9nc2VhX2ZpbGVjcmVhdGVfYmluYXJ5PC1mdW5jdGlvbihmcGttLmZpbGVwYXRoLGNsaW5pYy5maWxlcGF0aCx2YXIubmFtZXMsZ3JvdXAxLnZhbHVlcyxncm91cDIudmFsdWVzLGdyb3VwMS5uYW1lPSJncm91cDEiLGdyb3VwMi5uYW1lPSJncm91cDIiLG91dHB1dC5kaXIsc3VmZml4Lm5hbWUpewogICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiAgIyBBcmd1bWVudHM6CiAgIyAKICAjIGdleHAuZmlsZXBhdGggaXMgdGhlIGZpbGUgcGF0aCBvZiB0aGUgVFBNIG1hdHJpeCB3aXRoIGZpcnN0IDUgY29sdW1ucyBiZWluZyBhbm5vdGF0aW9uIGZvciBnZW5lcyAKICAjIChUUE1fYmFzZWxpbmVfMjc2X2NsZWFuX2NlbGxkaWZmYWRqdXN0ZWRfd2l0aGFubm90LnR4dCkuCiAgIyAKICAjIGNsaW5pYy5maWxwYXRoIGlzIHRoZSBmaWxlIHBhdGggb2YgdGhlIGNsaW5pYyBkYXRhIChjbGluaWNfbWF0cml4X21lcmdlZC50eHQpLiBUaGUgZmlyc3Qgcm93IG5lZWRzIHRvIGJlIHRoZSBjb2x1bW4gbmFtZXMuCiAgIyAKICAjIHZhci5uYW1lcyBpcyBhIHZlY3RvciBjb250YWluaW5nIHRoZSBuYW1lcyBvZiBjb2x1bW5zIGluIGNsaW5pYy5maWxlcGF0aCB0aGF0IHlvdSB3YW50IHRvIGdlbmVyYXRlIHRoZSBwaGVub3R5cGUgZmlsZSBmb3IuCiAgIwogICMgZ3JvdXAxLnZhbHVlcyBpcyBhIGRhdGEuZnJhbWUgb2Ygc3RyaW5nIGRlc2NyaWJpbmcgdmFsdWVzIG9mIHZhci5uYW1lcyB0aGF0IGFyZSBjb25zaWRlcmVkIGFzIGdvcnVwIDEuIAogICMgRm9yIGV4YW1wbGUsIGdyb3VwMS52YWx1ZXM9ZGF0YS5mcmFtZShQSEVOR1JQPSJub25hY3V0ZSBTdGFnZSBJIHVudHJlYXRlZCIpLiBOb3RlIHRoYXQgdGhlIGNvbHVtbnMgaW4gZ3JvdXAxLnZhbHVlcyBuZWVkIHRvIG1hdGNoIHRob3NlIGluIHZhci5uYW1lcyBpZiB0aGVyZSBhcmUKICAjIG1vcmUgdGhhbiAxIHZhcmlhYmxlIHRvIGRlZmluZSB0aGUgZ3JvdXBzLgogICMKICAjIGdyb3VwMi52YWx1ZXMgaGFzIHRoZSBzYW1lIGZvcm1hdCBhcyBncm91cDEudmFsdWVzIGJ1dCBpcyB0aGUgc2V0dGluZ3MgZm9yIGdyb3VwMi4KICAjCiAgIyBncm91cDEubmFtZSBpcyB0aGUgbmFtZSB5b3Ugd2FudCB0byBjYWxsIHlvdXIgZ3JvdXAxIGluIHRoZSBjbHMgZmlsZS4KICAjCiAgIyBncm91cDIubmFtZSBpcyB0aGUgbmFtZSB5b3Ugd2FudCB0byBjYWxsIHlvdXIgZ3JvdXAyIGluIHRoZSBjbHMgZmlsZS4KICAjIAogICMgb3V0cHV0LmRpciBpcyB0aGUgZm9sZGVyIG5hbWUgd2hlcmUgeW91IHdhbnQgdG8gc2F2ZSB0aGUgY3JlYXRlZCBmaWxlcy4KICAjCiAgIyBzdWZmaXgubmFtZSBpcyB0aGUgc3VmZml4IG5hbWUgaW4gdGhlIG5hbWUgb2YgdGhlIG91dHB1dCBmaWxlcy4gVGhlIGdjdCBmaWxlIHdpbGwgYmUgbmFtZWQgYXMgZ2N0X3N1ZmZpeC5uYW1lLmdjdCBhbmQgY2xzIGZpbGUgd2lsbCBiZSBuYW1lZCBhcwogICMgY2xzX3N1ZmZpeC5uYW1lLmdjdC4gRm9yIGV4YW1wbGUsIGlmIHN1ZmZpeC5uYW1lPSJ0ZXN0IiwgdGhlIGdjdCBmaWxlIHdpbGwgYmUgbmFtZWQgImdjdF90ZXN0LmdjdCIgYW5kIHRoZSBjbHMgZmlsZSB3aWxsIGJlIG5hbWVkIGFzIAogICMgImNsc190ZXN0LmNscyIuCiAgIyAKICAjIFZhbHVlOgogICMKICAjIFRoaXMgZnVuY3Rpb24gd2lsbCByZXR1cm4gMCBpZiBzdWNjZXNzZnVsbHkgcmFuLiBPdGhlcndpc2UsIGl0IHdpbGwgcmV0dXJuIDEuIElmIHVuc3VjY2Vzc2Z1bCwgaW5mb3JtYXRpb24gcmVnYXJkaW5nIHdoYXQgd2VudCB3cm9uZyB3aWxsIGJlIHNwaXQgCiAgIyBvdXQgYXMgd2FybmluZyBtZXNzYWdlcy4KICAjCiAgIyBXaGVuIHJhbiBzdWNjZXNzZnVsbHksIHRoaXMgZnVuY3Rpb24gd2lsbCBjcmVhdGUgdHdvIGZpbGVzIHVuZGVyIHRoZSBzcGVjaWZpZWQgb3V0cHV0LmRpciBuYW1lZCBnY3RfdmFyLm5hbWUuZ2N0IGFuZCBjbHNfdmFyLm5hbWUuY2xzLCB3aGVyZSAKICAjIHZhci5uYW1lIHdpbGwgYmUgdGhlIHNwZWZpY2llZCB2YWx1ZSAKICAjIGluIHRoZSBhcmd1bWVudC4gVGhlc2UgdHdvIGZpbGVzIGNhbiBiZSBkaXJlY3RseSBsb2FkZWQgaW50byBHU0VBIHRvZ2V0aGVyIHdpdGggYW5vdGhlciBnZW5lIHNldCBmaWxlIHRoYXQgbmVlZHMgdG8gYmUgZ2VuZXJhdGVkIG91dHNpZGUgb2YgdGhpcyAKICAjIGZ1bmN0aW9uLgogICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiAgCiAgIyAgZ2VuZXJhdGUgdGhlIGdlbmUgZXhwcmVzc2lvbiBtYXRyaXggaW5wdXQgZmlsZSBmb3IgR1NFQQogICNmcGttLmZpbGVwYXRoPC1maWxlLnBhdGgoaG9tZS5kaXIsInNjcmF0Y2gvR1JBRFMvU0FSQ19yZXN1bHRzL1Jlc3VsdHNfc3VtbWFyeV9QQk1DX2hnMzgvYmFzZWxpbmUvZGF0YV9hZGp1c3RlZDIiLCJUUE1fYmFzZWxpbmVfMjc2X2NsZWFuX2NlbGxkaWZmYWRqdXN0ZWRfd2l0aGFubm90LnR4dCIpCiAgI2NsaW5pYy5maWxlcGF0aDwtIi9ob21lL3lhbnhpdGluZy9kcml2ZXJfR3JhY2Uvc2NyYXRjaC9HUkFEUy9TQVJDX3Jlc3VsdHMvUmVzdWx0c19zdW1tYXJ5X1BCTUNfaGczOC9iYXNlbGluZS9kYXRhL2NsaW5pY19tYXRyaXhfbWVyZ2VkLnR4dCIKICAjb3V0cHV0LmRpcjwtIi9ob21lL3lhbnhpdGluZy9kcml2ZXJfR3JhY2Uvc2NyYXRjaC9HUkFEUy9TQVJDX3Jlc3VsdHMvUmVzdWx0c19zdW1tYXJ5X1BCTUNfaGczOC9iYXNlbGluZS9HU0VBX2tub3duZ2VuZXMiCiAgIyBzdWZmaXgubmFtZTwtImFuYWx5c2lzMSIKICAKICAKICAjIGxvYWQgaW4gdGhlIGZwa20gbWF0cml4CiAgZnBrbS5tYXRyaXg8LXJlYWQudGFibGUoZnBrbS5maWxlcGF0aCxzZXA9Ilx0IixoZWFkZXI9VCxjaGVjay5uYW1lcz1GLGFzLmlzPVRSVUUsY29tbWVudC5jaGFyID0gIiIpCiAgZnBrbS5tYXRyaXguYW5ubzwtZnBrbS5tYXRyaXhbLDE6Nl0KICBmcGttLm1hdHJpeDwtZnBrbS5tYXRyaXhbLDc6bmNvbChmcGttLm1hdHJpeCldCiAgCiAgIyBsb2FkIGluIHRoZSBjbGluaWNhbCBkYXRhIGFuZCBzdWJzZXQgdGhlIHNhbXBsZXMgdG8gdGhvc2UgbGlzdGVkIGluIHRoZSBmcGttLm1hdHJpeAogIGNsaW5pYy5tYXRyaXg9cmVhZC50YWJsZShjbGluaWMuZmlsZXBhdGgsc2VwPSJcdCIsaGVhZGVyPVQsY2hlY2submFtZXM9RixzdHJpbmdzQXNGYWN0b3JzPUYpCiAgcm93bmFtZXMoY2xpbmljLm1hdHJpeCk8LWFzLm1hdHJpeChjbGluaWMubWF0cml4KVssMV0gIyBHUkFEUyBJRAogIGNsaW5pYy5tYXRyaXg8LWNsaW5pYy5tYXRyaXhbY29sbmFtZXMoZnBrbS5tYXRyaXgpLF0KCiAgIyBiYXNlZCBvbiB2YXIubmFtZSwgZ3JvdXAxLnZhbHVlcywgYW5kIGdyb3VwMi52YWx1ZXMsIGlkZW50aWZ5IHRoZSBzYW1wbGVzIHRvIGluY2x1ZGUgaW4gdGhlIGZpbGVzLgogIGlmKGZpbGUuZXhpc3RzKG91dHB1dC5kaXIpPT1GKXsKICAgIGRpci5jcmVhdGUob3V0cHV0LmRpcikKICB9CiAgCiAgZ2N0LmZpbGVwYXRoPC1maWxlLnBhdGgob3V0cHV0LmRpcixwYXN0ZSgiZ2V4cF8iLHN1ZmZpeC5uYW1lLCIuZ2N0IixzZXA9IiIpKQogIGNscy5maWxlcGF0aDwtZmlsZS5wYXRoKG91dHB1dC5kaXIscGFzdGUoImNsc18iLHN1ZmZpeC5uYW1lLCIuY2xzIixzZXA9IiIpKQogIAogICMgc3Vic3RyYWN0IHNhbXBsZXMgd2l0aCB2YXIubmFtZSBlcXVhbCB0byB0aGUgdmFsdWVzIGluIGdyb3VwMS52YWx1ZXMgYW5kIGdyb3VwMi4gdmFsdWVzCiAgdGVtcDE8LWZwa20ubWF0cml4WyxhcHBseShhcy5kYXRhLmZyYW1lKGNsaW5pYy5tYXRyaXhbLHZhci5uYW1lc10pLDEscGFzdGUsY29sbGFwc2U9Il8iKSVpbiVhcHBseShncm91cDEudmFsdWVzLDEscGFzdGUsY29sbGFwc2U9Il8iKV0KICAjY2F0KCJcbiIpCiAgI2NhdChhcHBseShhcy5kYXRhLmZyYW1lKGNsaW5pYy5tYXRyaXhbLHZhci5uYW1lc10pLDEscGFzdGUsY29sbGFwc2U9Il8iKSkKICAjY2F0KCJcbiIpCiAgdGVtcDI8LWZwa20ubWF0cml4WyxhcHBseShhcy5kYXRhLmZyYW1lKGNsaW5pYy5tYXRyaXhbLHZhci5uYW1lc10pLDEscGFzdGUsY29sbGFwc2U9Il8iKSVpbiVhcHBseShncm91cDIudmFsdWVzLDEscGFzdGUsY29sbGFwc2U9Il8iKV0KICBjYXQoIlRoZXJlIGFyZSAiLG5jb2wodGVtcDEpLCIgYW5kICIsIG5jb2wodGVtcDIpLCIgc2FtcGxlcyBmb3IgZ3JvdXAxIGFuZCBncm91cDIsIHJlc3BlY3RpdmVseS5cbiIpCiAgZGF0YS5tYXRyaXg8LWNiaW5kKHRlbXAxLHRlbXAyKQogIGRhdGEubWF0cml4PC1jYmluZChmcGttLm1hdHJpeC5hbm5vWywxXSxyZXAoIm5hIixucm93KGRhdGEubWF0cml4KSksZGF0YS5tYXRyaXgpCiAgY29sbmFtZXMoZGF0YS5tYXRyaXgpWzE6Ml08LWMoIk5BTUUiLCJEZXNjcmlwdGlvbiIpCiAgcGhlbm8udmVjdDwtYyhyZXAoMCxuY29sKHRlbXAxKSkscmVwKDEsbmNvbCh0ZW1wMikpKQogIAogICMgb3V0cHV0IHRoZSBnZW5lIGV4cHJlc3Npb24gZGF0YQogIGNtZC5vdXQ8LSIjMS4yXG4iCiAgY21kLm91dDwtcGFzdGUoY21kLm91dCxucm93KGRhdGEubWF0cml4KSwiXHQiLG5jb2woZGF0YS5tYXRyaXgpLTIsIlxuIixzZXA9IiIpCiAgY2F0KGNtZC5vdXQsZmlsZT1nY3QuZmlsZXBhdGgsYXBwZW5kPUYpCiAgd3JpdGUudGFibGUoZGF0YS5tYXRyaXgsc2VwPSJcdCIsZmlsZT1nY3QuZmlsZXBhdGgsYXBwZW5kPVQscm93Lm5hbWVzPUYsY29sLm5hbWVzPVQscXVvdGU9RikKICAKICAjIG91dHB1dCB0aGUgcGhlbm90eXBlIGZpbGUKICBjbWQub3V0PC1wYXN0ZShuY29sKGRhdGEubWF0cml4KS0yLCIgIiwyLCIgIiwxLCJcbiIsc2VwPSIiKQogIGNtZC5vdXQ8LXBhc3RlKGNtZC5vdXQsIiMgZ3JvdXAxIGdyb3VwMlxuIixzZXA9IiIpCiAgY2F0KGNtZC5vdXQsZmlsZT1jbHMuZmlsZXBhdGgsYXBwZW5kPUYsc2VwPSIiKQogIGNhdChwaGVuby52ZWN0LGZpbGU9Y2xzLmZpbGVwYXRoLGFwcGVuZD1ULHNlcD0iICIpCiAgCiAgcmVzdWx0czwtbGlzdChncm91cDE9dGVtcDEsZ3JvdXAyPXRlbXAyKQogIAogIHJldHVybihyZXN1bHRzKQp9CgoKCmZwa20uZmlsZT1maWxlLnBhdGgoaG9tZS5kaXIsInNjcmF0Y2gvR1JBRFMvU0FSQ19yZXN1bHRzL1Jlc3VsdHNfc3VtbWFyeV9QQk1DX2hnMzgvYmFzZWxpbmUvZGF0YV9hZGp1c3RlZDIiLCJERVNlcTJfbm9ybWFsaXplZF8yNzZfY2xlYW5fbG9nMl9jZWxsZGlmZmFkanVzdGVkX3dpdGhhbm5vdC50eHQiKQpjbGluaWMuZmlsZTwtIi9ob21lL3lhbnhpdGluZy9kcml2ZXJfR3JhY2Uvc2NyYXRjaC9HUkFEUy9TQVJDX3Jlc3VsdHMvUmVzdWx0c19zdW1tYXJ5X1BCTUNfaGczOC9iYXNlbGluZS9kYXRhL2NsaW5pY19tYXRyaXhfbWVyZ2VkLnR4dCIKb3V0cHV0LmZvbGRlcjwtIi9ob21lL3lhbnhpdGluZy9kcml2ZXJfR3JhY2Uvc2NyYXRjaC9HUkFEUy9TQVJDX3Jlc3VsdHMvUmVzdWx0c19zdW1tYXJ5X1BCTUNfaGczOC9iYXNlbGluZS9HU0VBX2tub3duZ2VuZXNfREVTZXEyX2xvZzJhZGp1c3RlZCIKCiMgZ2VuZXJhdGUgdGhlIGZpbGVzIGZvciBhbmFseXNpcyAxCnZhcm5hbWVzPC1jKCJQSEVOR1JQIikKdGVtcDwtbXlfZ3NlYV9maWxlY3JlYXRlX2JpbmFyeShmcGttLmZpbGVwYXRoPWZwa20uZmlsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICBjbGluaWMuZmlsZXBhdGggPSBjbGluaWMuZmlsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICB2YXIubmFtZXMgPSB2YXJuYW1lcywKICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cDEudmFsdWVzID0gZGF0YS5mcmFtZShQSEVOR1JQPWMoIk5vbi1hY3V0ZSwgU3RhZ2UgSSwgdW50cmVhdGVkIikpLAogICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwMi52YWx1ZXMgPSBkYXRhLmZyYW1lKFBIRU5HUlA9YygiU3RhZ2UgSUktSUlJLCB1bnRyZWF0ZWQiLCJTdGFnZSBJViwgdW50cmVhdGVkIikpLAogICAgICAgICAgICAgICAgICAgICAgICAgIG91dHB1dC5kaXI9b3V0cHV0LmZvbGRlciwKICAgICAgICAgICAgICAgICAgICAgICAgICBzdWZmaXgubmFtZT0iYW5hbHlzaXMxIgogICAgICAgICAgICAgICAgICAgICAgICAgICkKCiMgZ2VuZXJhdGUgdGhlIGZpbGVzIGZvciBhbmFseXNpcyAyCnZhcm5hbWVzPC1jKCJQSEVOR1JQIikKdGVtcDwtbXlfZ3NlYV9maWxlY3JlYXRlX2JpbmFyeShmcGttLmZpbGVwYXRoPWZwa20uZmlsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICBjbGluaWMuZmlsZXBhdGggPSBjbGluaWMuZmlsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICB2YXIubmFtZXMgPSB2YXJuYW1lcywKICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cDEudmFsdWVzID0gZGF0YS5mcmFtZShQSEVOR1JQPWMoIlJlbWl0dGluZywgdW50cmVhdGVkIikpLAogICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwMi52YWx1ZXMgPSBkYXRhLmZyYW1lKFBIRU5HUlA9YygiTm9uLWFjdXRlLCBTdGFnZSBJLCB1bnRyZWF0ZWQiLCJTdGFnZSBJSS1JSUksIHVudHJlYXRlZCIsIlN0YWdlIElWLCB1bnRyZWF0ZWQiKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgb3V0cHV0LmRpcj1vdXRwdXQuZm9sZGVyLAogICAgICAgICAgICAgICAgICAgICAgICAgIHN1ZmZpeC5uYW1lPSJhbmFseXNpczIiCiAgICAgICAgICAgICAgICAgICAgICAgICAgKQoKIyBnZW5lcmF0ZSB0aGUgZmlsZXMgZm9yIGFuYWx5c2lzIDMKdmFybmFtZXM8LWMoIlBIRU5HUlAiKQp0ZW1wPC1teV9nc2VhX2ZpbGVjcmVhdGVfYmluYXJ5KGZwa20uZmlsZXBhdGg9ZnBrbS5maWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgIGNsaW5pYy5maWxlcGF0aCA9IGNsaW5pYy5maWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgIHZhci5uYW1lcyA9IHZhcm5hbWVzLAogICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwMS52YWx1ZXMgPSBkYXRhLmZyYW1lKFBIRU5HUlA9YygiU3RhZ2UgSVYsIHVudHJlYXRlZCIpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cDIudmFsdWVzID0gZGF0YS5mcmFtZShQSEVOR1JQPWMoIk5vbi1hY3V0ZSwgU3RhZ2UgSSwgdW50cmVhdGVkIiwiU3RhZ2UgSUktSUlJLCB1bnRyZWF0ZWQiKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgb3V0cHV0LmRpcj1vdXRwdXQuZm9sZGVyLAogICAgICAgICAgICAgICAgICAgICAgICAgIHN1ZmZpeC5uYW1lPSJhbmFseXNpczMiCiAgICAgICAgICAgICAgICAgICAgICAgICAgKQojIGdlbmVyYXRlIHRoZSBmaWxlcyBmb3IgYW5hbHlzaXMgNC4gSWYgd2UgZGlyZWN0bHkgc3BlY2lmeSBncm91cDEudmFsdWVzIHVzaW5nIG51bWJlcnMsIGl0IHdvbid0IHdvcmsuIEkgaGFkIHRvIHNwZWNpZnkgdGhlbSB0byBiZSBhIHNwYWNlK251bWJlciBhcyBhIGNoYXJhY3Rlci4KdmFybmFtZXM8LWMoIm5vZHVsZV9seW1waF9waGVubyIsInN0ZXJvaWRfYXR2MSIsImRtYXJkX2F0djEiKQp0ZW1wPC1teV9nc2VhX2ZpbGVjcmVhdGVfYmluYXJ5KGZwa20uZmlsZXBhdGg9ZnBrbS5maWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgIGNsaW5pYy5maWxlcGF0aCA9IGNsaW5pYy5maWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgIHZhci5uYW1lcyA9IHZhcm5hbWVzLAogICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwMS52YWx1ZXMgPSBkYXRhLmZyYW1lKG5vZHVsZV9seW1waF9waGVubz1jKCJseW1waCIpLHN0ZXJvaWRfYXR2MT1jKCIgMCIpLGRtYXJkX2F0djE9YygiIDAiKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAyLnZhbHVlcyA9IGRhdGEuZnJhbWUobm9kdWxlX2x5bXBoX3BoZW5vPWMoIm1pY3Jvbm9kdWxlIiwiYm90aCIpLHN0ZXJvaWRfYXR2MT1jKCIgMCIsIiAwIiksZG1hcmRfYXR2MT1jKCIgMCIsIiAwIikpLAogICAgICAgICAgICAgICAgICAgICAgICAgIG91dHB1dC5kaXI9b3V0cHV0LmZvbGRlciwKICAgICAgICAgICAgICAgICAgICAgICAgICBzdWZmaXgubmFtZT0iYW5hbHlzaXM0IgogICAgICAgICAgICAgICAgICAgICAgICAgICkKCiMgZ2VuZXJhdGUgdGhlIGZpbGVzIGZvciBhbmFseXNpcyA1LiAKdmFybmFtZXM8LWMoIlBIRU5HUlAiKQp0ZW1wPC1teV9nc2VhX2ZpbGVjcmVhdGVfYmluYXJ5KGZwa20uZmlsZXBhdGg9ZnBrbS5maWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgIGNsaW5pYy5maWxlcGF0aCA9IGNsaW5pYy5maWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgIHZhci5uYW1lcyA9IHZhcm5hbWVzLAogICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwMS52YWx1ZXMgPSBkYXRhLmZyYW1lKFBIRU5HUlA9YygiUmVtaXR0aW5nLCB1bnRyZWF0ZWQiKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAyLnZhbHVlcyA9IGRhdGEuZnJhbWUoUEhFTkdSUD1jKCJTdGFnZSBJSS1JSUksIHVudHJlYXRlZCIpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBvdXRwdXQuZGlyPW91dHB1dC5mb2xkZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgc3VmZml4Lm5hbWU9ImFuYWx5c2lzNSIKICAgICAgICAgICAgICAgICAgICAgICAgICApCgoKIyBnZW5lcmF0ZSB0aGUgZmlsZXMgZm9yIGFuYWx5c2lzIDRhLiBJZiB3ZSBkaXJlY3RseSBzcGVjaWZ5IGdyb3VwMS52YWx1ZXMgdXNpbmcgbnVtYmVycywgaXQgd29uJ3Qgd29yay4gSSBoYWQgdG8gc3BlY2lmeSB0aGVtIHRvIGJlIGEgc3BhY2UrbnVtYmVyIGFzIGEgY2hhcmFjdGVyLgpjbGluaWMuZmlsZXBhdGg8LSIvaG9tZS95YW54aXRpbmcvZHJpdmVyX0dyYWNlL3NjcmF0Y2gvR1JBRFMvU0FSQ19yZXN1bHRzL1Jlc3VsdHNfc3VtbWFyeV9QQk1DX2hnMzgvYmFzZWxpbmUvZGF0YS9jbGluaWNfY3RfbWVyZ2VfbWF0cml4X3dpdGhnZXguUkRTIgpjbGluaWMubWF0cml4PC1yZWFkUkRTKGNsaW5pYy5maWxlcGF0aCxyZWZob29rID0gTlVMTCkKY2xpbmljLmZpbGU8LSIvaG9tZS95YW54aXRpbmcvZHJpdmVyX0dyYWNlL3NjcmF0Y2gvR1JBRFMvU0FSQ19yZXN1bHRzL1Jlc3VsdHNfc3VtbWFyeV9QQk1DX2hnMzgvYmFzZWxpbmUvZGF0YS9jbGluaWNfY3RfbWVyZ2VfbWF0cml4X3dpdGhnZXgudHh0IgoKdmFybmFtZXM8LWMoIm5vZHVsZV9seW1waF9waGVubyIsInN0ZXJvaWRfYXR2MSIsImRtYXJkX2F0djEiLCJHcm91bmRfR2xhc3MiLCJIb25leWNvbWJpbmciLCJSZXRpY3VsYXJfQWJub3JtYWxpdHkiKQp0ZW1wPC1teV9nc2VhX2ZpbGVjcmVhdGVfYmluYXJ5KGZwa20uZmlsZXBhdGg9ZnBrbS5maWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgIGNsaW5pYy5maWxlcGF0aCA9IGNsaW5pYy5maWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgIHZhci5uYW1lcyA9IHZhcm5hbWVzLAogICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwMS52YWx1ZXMgPSBkYXRhLmZyYW1lKG5vZHVsZV9seW1waF9waGVubz1jKCJseW1waCIpLHN0ZXJvaWRfYXR2MT1jKCIgMCIpLGRtYXJkX2F0djE9YygiIDAiKSxHcm91bmRfR2xhc3M9YygiIDAiKSxIb25leWNvbWJpbmc9YygiIDAiKSxSZXRpY3VsYXJfQWJub3JtYWxpdHk9YygiIDAiKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAyLnZhbHVlcyA9IGRhdGEuZnJhbWUobm9kdWxlX2x5bXBoX3BoZW5vPWMoIm1pY3Jvbm9kdWxlIiwiYm90aCIpLHN0ZXJvaWRfYXR2MT1jKCIgMCIsIiAwIiksZG1hcmRfYXR2MT1jKCIgMCIsIiAwIiksR3JvdW5kX0dsYXNzPWMoIiAwIiwiIDAiKSxIb25leWNvbWJpbmc9YygiIDAiLCIgMCIpLFJldGljdWxhcl9BYm5vcm1hbGl0eT1jKCIgMCIsIiAwIikpLAogICAgICAgICAgICAgICAgICAgICAgICAgIG91dHB1dC5kaXI9b3V0cHV0LmZvbGRlciwKICAgICAgICAgICAgICAgICAgICAgICAgICBzdWZmaXgubmFtZT0iYW5hbHlzaXM0YSIKICAgICAgICAgICAgICAgICAgICAgICAgICApCgojIGdlbmVyYXRlIHRoZSBmaWxlcyBmb3IgYW5hbHlzaXMgNGIuIElmIHdlIGRpcmVjdGx5IHNwZWNpZnkgZ3JvdXAxLnZhbHVlcyB1c2luZyBudW1iZXJzLCBpdCB3b24ndCB3b3JrLiBJIGhhZCB0byBzcGVjaWZ5IHRoZW0gdG8gYmUgYSBzcGFjZStudW1iZXIgYXMgYSBjaGFyYWN0ZXIuCmNsaW5pYy5maWxlcGF0aDwtIi9ob21lL3lhbnhpdGluZy9kcml2ZXJfR3JhY2Uvc2NyYXRjaC9HUkFEUy9TQVJDX3Jlc3VsdHMvUmVzdWx0c19zdW1tYXJ5X1BCTUNfaGczOC9iYXNlbGluZS9kYXRhL2NsaW5pY19jdF9tZXJnZV9tYXRyaXhfd2l0aGdleC5SRFMiCmNsaW5pYy5tYXRyaXg8LXJlYWRSRFMoY2xpbmljLmZpbGVwYXRoLHJlZmhvb2sgPSBOVUxMKQpjbGluaWMuZmlsZTwtIi9ob21lL3lhbnhpdGluZy9kcml2ZXJfR3JhY2Uvc2NyYXRjaC9HUkFEUy9TQVJDX3Jlc3VsdHMvUmVzdWx0c19zdW1tYXJ5X1BCTUNfaGczOC9iYXNlbGluZS9kYXRhL2NsaW5pY19jdF9tZXJnZV9tYXRyaXhfd2l0aGdleC50eHQiCgp2YXJuYW1lczwtYygibm9kdWxlX2x5bXBoX3BoZW5vIiwic3Rlcm9pZF9hdHYxIiwiZG1hcmRfYXR2MSIsIkdyb3VuZF9HbGFzcyIsIkhvbmV5Y29tYmluZyIsIlJldGljdWxhcl9BYm5vcm1hbGl0eSIpCnRlbXA8LW15X2dzZWFfZmlsZWNyZWF0ZV9iaW5hcnkoZnBrbS5maWxlcGF0aD1mcGttLmZpbGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgY2xpbmljLmZpbGVwYXRoID0gY2xpbmljLmZpbGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyLm5hbWVzID0gdmFybmFtZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAxLnZhbHVlcyA9IGRhdGEuZnJhbWUobm9kdWxlX2x5bXBoX3BoZW5vPWMoImx5bXBoIiksc3Rlcm9pZF9hdHYxPWMoIiAwIiksZG1hcmRfYXR2MT1jKCIgMCIpLEdyb3VuZF9HbGFzcz1jKCIgMCIpLEhvbmV5Y29tYmluZz1jKCIgMCIpLFJldGljdWxhcl9BYm5vcm1hbGl0eT1jKCIgMCIpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cDIudmFsdWVzID0gZGF0YS5mcmFtZShub2R1bGVfbHltcGhfcGhlbm89YygibWljcm9ub2R1bGUiKSxzdGVyb2lkX2F0djE9YygiIDAiKSxkbWFyZF9hdHYxPWMoIiAwIiksR3JvdW5kX0dsYXNzPWMoIiAwIiksSG9uZXljb21iaW5nPWMoIiAwIiksUmV0aWN1bGFyX0Fibm9ybWFsaXR5PWMoIiAwIikpLAogICAgICAgICAgICAgICAgICAgICAgICAgIG91dHB1dC5kaXI9b3V0cHV0LmZvbGRlciwKICAgICAgICAgICAgICAgICAgICAgICAgICBzdWZmaXgubmFtZT0iYW5hbHlzaXM0YiIKICAgICAgICAgICAgICAgICAgICAgICAgICApCmBgYAoKV2UgYWxzbyBnZW5lcmF0ZSBHU0VBIGlucHV0IGZpbGVzIGZvciBwYXRpZW50cyBmcm9tIGdpdmVuIGxpc3RzIGluY2x1ZGluZyB0aGUgZm9sbG93aW5nOgoKKiBTdGFnZSBJLCB1bnRyZWF0ZWQgYW5kIGx5bXBoYWRlbm9wYXRoeSBvbmx5IG92ZXJsYXBwZWQgcGF0aWVudHMgCiogU3RhZ2UgSSwgdW50cmVhZCBvbmx5IHBhdGllbnRzCiogbHltcGhhZGVub3BhdGh5IG9ubHkgcGF0aWVudHMKClRoZXNlIHRocmVlIGFuYWx5c2lzIGlzIHRvIHNlZSBpZiB0aGUgb3ZlcmxhcHBlZCBwYXRpZW50cyBkcm92ZSB0aGUgc2lnbmlmaWNhbnQgcmVzdWx0cy4KCmBgYHtyfQpncmFkcy5pZC5saXN0PC1maWxlLnBhdGgoaG9tZS5kaXIsInNjcmF0Y2gvR1JBRFMvU0FSQ19yZXN1bHRzL1Jlc3VsdHNfc3VtbWFyeV9QQk1DX2hnMzgvYmFzZWxpbmUvR1NFQV9rbm93bmdlbmVzX0RFU2VxMl9sb2cyYWRqdXN0ZWQvZ3JhZHNpZF9vdmVybGFwX25vbm92ZXJsYXAueGxzeCIpCmdyYWRzLmlkLm1hdHJpeDwtcmVhZC54bHMoZ3JhZHMuaWQubGlzdCxzaGVldD0xKQpvdmVybGFwLmlkPC1hcy5jaGFyYWN0ZXIoZ3JhZHMuaWQubWF0cml4WywxXSlbYXMuY2hhcmFjdGVyKGdyYWRzLmlkLm1hdHJpeFssMV0pIT0iIl0Kc3RhZ2Uxb25seS5pZDwtYXMuY2hhcmFjdGVyKGdyYWRzLmlkLm1hdHJpeFssMl0pW2FzLmNoYXJhY3RlcihncmFkcy5pZC5tYXRyaXhbLDJdKSE9IiJdCmx5bXBob25seS5pZDwtYXMuY2hhcmFjdGVyKGdyYWRzLmlkLm1hdHJpeFssM10pW2FzLmNoYXJhY3RlcihncmFkcy5pZC5tYXRyaXhbLDNdKSE9IiJdCgoKIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBQYXJ0IDE6IDEyIHN0YWdlIEksIHVudHJlYXRlZCBhbmQgbHltcGggb25seSBwYXRpZW50cwojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIGxvYWQgaW4gdGhlIGRhdGEKZnBrbS5maWxlPWZpbGUucGF0aChob21lLmRpciwic2NyYXRjaC9HUkFEUy9TQVJDX3Jlc3VsdHMvUmVzdWx0c19zdW1tYXJ5X1BCTUNfaGczOC9iYXNlbGluZS9kYXRhX2FkanVzdGVkMiIsIkRFU2VxMl9ub3JtYWxpemVkXzI3Nl9jbGVhbl9sb2cyX2NlbGxkaWZmYWRqdXN0ZWRfd2l0aGFubm90LnR4dCIpCmNsaW5pYy5maWxlPC0iL2hvbWUveWFueGl0aW5nL2RyaXZlcl9HcmFjZS9zY3JhdGNoL0dSQURTL1NBUkNfcmVzdWx0cy9SZXN1bHRzX3N1bW1hcnlfUEJNQ19oZzM4L2Jhc2VsaW5lL2RhdGEvY2xpbmljX21hdHJpeF9tZXJnZWQudHh0IgpvdXRwdXQuZm9sZGVyPC0iL2hvbWUveWFueGl0aW5nL2RyaXZlcl9HcmFjZS9zY3JhdGNoL0dSQURTL1NBUkNfcmVzdWx0cy9SZXN1bHRzX3N1bW1hcnlfUEJNQ19oZzM4L2Jhc2VsaW5lL0dTRUFfa25vd25nZW5lc19ERVNlcTJfbG9nMmFkanVzdGVkIgpzdWZmaXgubmFtZTwtImFuYWx5c2lzNiIKdmFyLm5hbWVzPWMoIlBIRU5HUlAiKQpncm91cDIudmFsdWVzID0gZGF0YS5mcmFtZShQSEVOR1JQPWMoIlN0YWdlIElJLUlJSSwgdW50cmVhdGVkIiwiU3RhZ2UgSVYsIHVudHJlYXRlZCIpKQoKIyBsb2FkIGluIHRoZSBmcGttIG1hdHJpeApmcGttLm1hdHJpeDwtcmVhZC50YWJsZShmcGttLmZpbGUsc2VwPSJcdCIsaGVhZGVyPVQsY2hlY2submFtZXM9Rixhcy5pcz1UUlVFLGNvbW1lbnQuY2hhciA9ICIiKQpmcGttLm1hdHJpeC5hbm5vPC1mcGttLm1hdHJpeFssMTo2XQpmcGttLm1hdHJpeDwtZnBrbS5tYXRyaXhbLDc6bmNvbChmcGttLm1hdHJpeCldCiAgCiMgbG9hZCBpbiB0aGUgY2xpbmljYWwgZGF0YSBhbmQgc3Vic2V0IHRoZSBzYW1wbGVzIHRvIHRob3NlIGxpc3RlZCBpbiB0aGUgZnBrbS5tYXRyaXgKY2xpbmljLm1hdHJpeD1yZWFkLnRhYmxlKGNsaW5pYy5maWxlLHNlcD0iXHQiLGhlYWRlcj1ULGNoZWNrLm5hbWVzPUYsc3RyaW5nc0FzRmFjdG9ycz1GKQpyb3duYW1lcyhjbGluaWMubWF0cml4KTwtYXMubWF0cml4KGNsaW5pYy5tYXRyaXgpWywxXSAjIEdSQURTIElECmNsaW5pYy5tYXRyaXg8LWNsaW5pYy5tYXRyaXhbY29sbmFtZXMoZnBrbS5tYXRyaXgpLF0KCiMgYmFzZWQgb24gdmFyLm5hbWUsIGdyb3VwMS52YWx1ZXMsIGFuZCBncm91cDIudmFsdWVzLCBpZGVudGlmeSB0aGUgc2FtcGxlcyB0byBpbmNsdWRlIGluIHRoZSBmaWxlcy4KaWYoZmlsZS5leGlzdHMob3V0cHV0LmZvbGRlcik9PUYpewogIGRpci5jcmVhdGUob3V0cHV0LmZvbGRlcikKfQogIApnY3QuZmlsZXBhdGg8LWZpbGUucGF0aChvdXRwdXQuZm9sZGVyLHBhc3RlKCJnZXhwXyIsc3VmZml4Lm5hbWUsIi5nY3QiLHNlcD0iIikpCmNscy5maWxlcGF0aDwtZmlsZS5wYXRoKG91dHB1dC5mb2xkZXIscGFzdGUoImNsc18iLHN1ZmZpeC5uYW1lLCIuY2xzIixzZXA9IiIpKQogIAojIHN1YnN0cmFjdCBzYW1wbGVzIHdpdGggdmFyLm5hbWUgZXF1YWwgdG8gdGhlIHZhbHVlcyBpbiBncm91cDEudmFsdWVzIGFuZCBncm91cDIuIHZhbHVlcwojdGVtcDE8LWZwa20ubWF0cml4WyxhcHBseShhcy5kYXRhLmZyYW1lKGNsaW5pYy5tYXRyaXhbLHZhci5uYW1lc10pLDEscGFzdGUsY29sbGFwc2U9Il8iKSVpbiVhcHBseShncm91cDEudmFsdWVzLDEscGFzdGUsY29sbGFwc2U9Il8iKV0KdGVtcDE8LWZwa20ubWF0cml4WyxvdmVybGFwLmlkXQp0ZW1wMjwtZnBrbS5tYXRyaXhbLGFwcGx5KGFzLmRhdGEuZnJhbWUoY2xpbmljLm1hdHJpeFssdmFyLm5hbWVzXSksMSxwYXN0ZSxjb2xsYXBzZT0iXyIpJWluJWFwcGx5KGdyb3VwMi52YWx1ZXMsMSxwYXN0ZSxjb2xsYXBzZT0iXyIpXQoKY2F0KCJUaGVyZSBhcmUgIixuY29sKHRlbXAxKSwiIGFuZCAiLCBuY29sKHRlbXAyKSwiIHNhbXBsZXMgZm9yIGdyb3VwMSBhbmQgZ3JvdXAyLCByZXNwZWN0aXZlbHkuXG4iKQpkYXRhLm1hdHJpeDwtY2JpbmQodGVtcDEsdGVtcDIpCmRhdGEubWF0cml4PC1jYmluZChmcGttLm1hdHJpeC5hbm5vWywxXSxyZXAoIm5hIixucm93KGRhdGEubWF0cml4KSksZGF0YS5tYXRyaXgpCmNvbG5hbWVzKGRhdGEubWF0cml4KVsxOjJdPC1jKCJOQU1FIiwiRGVzY3JpcHRpb24iKQpwaGVuby52ZWN0PC1jKHJlcCgwLG5jb2wodGVtcDEpKSxyZXAoMSxuY29sKHRlbXAyKSkpCgojIG91dHB1dCB0aGUgZ2VuZSBleHByZXNzaW9uIGRhdGEKY21kLm91dDwtIiMxLjJcbiIKY21kLm91dDwtcGFzdGUoY21kLm91dCxucm93KGRhdGEubWF0cml4KSwiXHQiLG5jb2woZGF0YS5tYXRyaXgpLTIsIlxuIixzZXA9IiIpCmNhdChjbWQub3V0LGZpbGU9Z2N0LmZpbGVwYXRoLGFwcGVuZD1GKQp3cml0ZS50YWJsZShkYXRhLm1hdHJpeCxzZXA9Ilx0IixmaWxlPWdjdC5maWxlcGF0aCxhcHBlbmQ9VCxyb3cubmFtZXM9Rixjb2wubmFtZXM9VCxxdW90ZT1GKQoKIyBvdXRwdXQgdGhlIHBoZW5vdHlwZSBmaWxlCmNtZC5vdXQ8LXBhc3RlKG5jb2woZGF0YS5tYXRyaXgpLTIsIiAiLDIsIiAiLDEsIlxuIixzZXA9IiIpCmNtZC5vdXQ8LXBhc3RlKGNtZC5vdXQsIiMgZ3JvdXAxIGdyb3VwMlxuIixzZXA9IiIpCmNhdChjbWQub3V0LGZpbGU9Y2xzLmZpbGVwYXRoLGFwcGVuZD1GLHNlcD0iIikKY2F0KHBoZW5vLnZlY3QsZmlsZT1jbHMuZmlsZXBhdGgsYXBwZW5kPVQsc2VwPSIgIikKIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgcGFydCAyOiBzdGFnZSBJIG9ubHkKIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBsb2FkIGluIHRoZSBkYXRhCmZwa20uZmlsZT1maWxlLnBhdGgoaG9tZS5kaXIsInNjcmF0Y2gvR1JBRFMvU0FSQ19yZXN1bHRzL1Jlc3VsdHNfc3VtbWFyeV9QQk1DX2hnMzgvYmFzZWxpbmUvZGF0YV9hZGp1c3RlZDIiLCJERVNlcTJfbm9ybWFsaXplZF8yNzZfY2xlYW5fbG9nMl9jZWxsZGlmZmFkanVzdGVkX3dpdGhhbm5vdC50eHQiKQpjbGluaWMuZmlsZTwtIi9ob21lL3lhbnhpdGluZy9kcml2ZXJfR3JhY2Uvc2NyYXRjaC9HUkFEUy9TQVJDX3Jlc3VsdHMvUmVzdWx0c19zdW1tYXJ5X1BCTUNfaGczOC9iYXNlbGluZS9kYXRhL2NsaW5pY19tYXRyaXhfbWVyZ2VkLnR4dCIKb3V0cHV0LmZvbGRlcjwtIi9ob21lL3lhbnhpdGluZy9kcml2ZXJfR3JhY2Uvc2NyYXRjaC9HUkFEUy9TQVJDX3Jlc3VsdHMvUmVzdWx0c19zdW1tYXJ5X1BCTUNfaGczOC9iYXNlbGluZS9HU0VBX2tub3duZ2VuZXNfREVTZXEyX2xvZzJhZGp1c3RlZCIKc3VmZml4Lm5hbWU8LSJhbmFseXNpczciCnZhci5uYW1lcz1jKCJQSEVOR1JQIikKZ3JvdXAyLnZhbHVlcyA9IGRhdGEuZnJhbWUoUEhFTkdSUD1jKCJTdGFnZSBJSS1JSUksIHVudHJlYXRlZCIsIlN0YWdlIElWLCB1bnRyZWF0ZWQiKSkKCiMgbG9hZCBpbiB0aGUgZnBrbSBtYXRyaXgKZnBrbS5tYXRyaXg8LXJlYWQudGFibGUoZnBrbS5maWxlLHNlcD0iXHQiLGhlYWRlcj1ULGNoZWNrLm5hbWVzPUYsYXMuaXM9VFJVRSxjb21tZW50LmNoYXIgPSAiIikKZnBrbS5tYXRyaXguYW5ubzwtZnBrbS5tYXRyaXhbLDE6Nl0KZnBrbS5tYXRyaXg8LWZwa20ubWF0cml4Wyw3Om5jb2woZnBrbS5tYXRyaXgpXQogIAojIGxvYWQgaW4gdGhlIGNsaW5pY2FsIGRhdGEgYW5kIHN1YnNldCB0aGUgc2FtcGxlcyB0byB0aG9zZSBsaXN0ZWQgaW4gdGhlIGZwa20ubWF0cml4CmNsaW5pYy5tYXRyaXg9cmVhZC50YWJsZShjbGluaWMuZmlsZSxzZXA9Ilx0IixoZWFkZXI9VCxjaGVjay5uYW1lcz1GLHN0cmluZ3NBc0ZhY3RvcnM9RikKcm93bmFtZXMoY2xpbmljLm1hdHJpeCk8LWFzLm1hdHJpeChjbGluaWMubWF0cml4KVssMV0gIyBHUkFEUyBJRApjbGluaWMubWF0cml4PC1jbGluaWMubWF0cml4W2NvbG5hbWVzKGZwa20ubWF0cml4KSxdCgojIGJhc2VkIG9uIHZhci5uYW1lLCBncm91cDEudmFsdWVzLCBhbmQgZ3JvdXAyLnZhbHVlcywgaWRlbnRpZnkgdGhlIHNhbXBsZXMgdG8gaW5jbHVkZSBpbiB0aGUgZmlsZXMuCmlmKGZpbGUuZXhpc3RzKG91dHB1dC5mb2xkZXIpPT1GKXsKICBkaXIuY3JlYXRlKG91dHB1dC5mb2xkZXIpCn0KICAKZ2N0LmZpbGVwYXRoPC1maWxlLnBhdGgob3V0cHV0LmZvbGRlcixwYXN0ZSgiZ2V4cF8iLHN1ZmZpeC5uYW1lLCIuZ2N0IixzZXA9IiIpKQpjbHMuZmlsZXBhdGg8LWZpbGUucGF0aChvdXRwdXQuZm9sZGVyLHBhc3RlKCJjbHNfIixzdWZmaXgubmFtZSwiLmNscyIsc2VwPSIiKSkKICAKIyBzdWJzdHJhY3Qgc2FtcGxlcyB3aXRoIHZhci5uYW1lIGVxdWFsIHRvIHRoZSB2YWx1ZXMgaW4gZ3JvdXAxLnZhbHVlcyBhbmQgZ3JvdXAyLiB2YWx1ZXMKI3RlbXAxPC1mcGttLm1hdHJpeFssYXBwbHkoYXMuZGF0YS5mcmFtZShjbGluaWMubWF0cml4Wyx2YXIubmFtZXNdKSwxLHBhc3RlLGNvbGxhcHNlPSJfIiklaW4lYXBwbHkoZ3JvdXAxLnZhbHVlcywxLHBhc3RlLGNvbGxhcHNlPSJfIildCnRlbXAxPC1mcGttLm1hdHJpeFssc3RhZ2Uxb25seS5pZF0KdGVtcDI8LWZwa20ubWF0cml4WyxhcHBseShhcy5kYXRhLmZyYW1lKGNsaW5pYy5tYXRyaXhbLHZhci5uYW1lc10pLDEscGFzdGUsY29sbGFwc2U9Il8iKSVpbiVhcHBseShncm91cDIudmFsdWVzLDEscGFzdGUsY29sbGFwc2U9Il8iKV0KCmNhdCgiVGhlcmUgYXJlICIsbmNvbCh0ZW1wMSksIiBhbmQgIiwgbmNvbCh0ZW1wMiksIiBzYW1wbGVzIGZvciBncm91cDEgYW5kIGdyb3VwMiwgcmVzcGVjdGl2ZWx5LlxuIikKZGF0YS5tYXRyaXg8LWNiaW5kKHRlbXAxLHRlbXAyKQpkYXRhLm1hdHJpeDwtY2JpbmQoZnBrbS5tYXRyaXguYW5ub1ssMV0scmVwKCJuYSIsbnJvdyhkYXRhLm1hdHJpeCkpLGRhdGEubWF0cml4KQpjb2xuYW1lcyhkYXRhLm1hdHJpeClbMToyXTwtYygiTkFNRSIsIkRlc2NyaXB0aW9uIikKcGhlbm8udmVjdDwtYyhyZXAoMCxuY29sKHRlbXAxKSkscmVwKDEsbmNvbCh0ZW1wMikpKQoKIyBvdXRwdXQgdGhlIGdlbmUgZXhwcmVzc2lvbiBkYXRhCmNtZC5vdXQ8LSIjMS4yXG4iCmNtZC5vdXQ8LXBhc3RlKGNtZC5vdXQsbnJvdyhkYXRhLm1hdHJpeCksIlx0IixuY29sKGRhdGEubWF0cml4KS0yLCJcbiIsc2VwPSIiKQpjYXQoY21kLm91dCxmaWxlPWdjdC5maWxlcGF0aCxhcHBlbmQ9RikKd3JpdGUudGFibGUoZGF0YS5tYXRyaXgsc2VwPSJcdCIsZmlsZT1nY3QuZmlsZXBhdGgsYXBwZW5kPVQscm93Lm5hbWVzPUYsY29sLm5hbWVzPVQscXVvdGU9RikKCiMgb3V0cHV0IHRoZSBwaGVub3R5cGUgZmlsZQpjbWQub3V0PC1wYXN0ZShuY29sKGRhdGEubWF0cml4KS0yLCIgIiwyLCIgIiwxLCJcbiIsc2VwPSIiKQpjbWQub3V0PC1wYXN0ZShjbWQub3V0LCIjIGdyb3VwMSBncm91cDJcbiIsc2VwPSIiKQpjYXQoY21kLm91dCxmaWxlPWNscy5maWxlcGF0aCxhcHBlbmQ9RixzZXA9IiIpCmNhdChwaGVuby52ZWN0LGZpbGU9Y2xzLmZpbGVwYXRoLGFwcGVuZD1ULHNlcD0iICIpCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgcGFydCAzOiBseW1waCBvbmx5CiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgbG9hZCBpbiB0aGUgZGF0YQpmcGttLmZpbGU9ZmlsZS5wYXRoKGhvbWUuZGlyLCJzY3JhdGNoL0dSQURTL1NBUkNfcmVzdWx0cy9SZXN1bHRzX3N1bW1hcnlfUEJNQ19oZzM4L2Jhc2VsaW5lL2RhdGFfYWRqdXN0ZWQyIiwiREVTZXEyX25vcm1hbGl6ZWRfMjc2X2NsZWFuX2xvZzJfY2VsbGRpZmZhZGp1c3RlZF93aXRoYW5ub3QudHh0IikKY2xpbmljLmZpbGU8LSIvaG9tZS95YW54aXRpbmcvZHJpdmVyX0dyYWNlL3NjcmF0Y2gvR1JBRFMvU0FSQ19yZXN1bHRzL1Jlc3VsdHNfc3VtbWFyeV9QQk1DX2hnMzgvYmFzZWxpbmUvZGF0YS9jbGluaWNfbWF0cml4X21lcmdlZC50eHQiCm91dHB1dC5mb2xkZXI8LSIvaG9tZS95YW54aXRpbmcvZHJpdmVyX0dyYWNlL3NjcmF0Y2gvR1JBRFMvU0FSQ19yZXN1bHRzL1Jlc3VsdHNfc3VtbWFyeV9QQk1DX2hnMzgvYmFzZWxpbmUvR1NFQV9rbm93bmdlbmVzX0RFU2VxMl9sb2cyYWRqdXN0ZWQiCnN1ZmZpeC5uYW1lPC0iYW5hbHlzaXM4Igp2YXIubmFtZXM9YygiUEhFTkdSUCIpCmdyb3VwMi52YWx1ZXMgPSBkYXRhLmZyYW1lKFBIRU5HUlA9YygiU3RhZ2UgSUktSUlJLCB1bnRyZWF0ZWQiLCJTdGFnZSBJViwgdW50cmVhdGVkIikpCgojIGxvYWQgaW4gdGhlIGZwa20gbWF0cml4CmZwa20ubWF0cml4PC1yZWFkLnRhYmxlKGZwa20uZmlsZSxzZXA9Ilx0IixoZWFkZXI9VCxjaGVjay5uYW1lcz1GLGFzLmlzPVRSVUUsY29tbWVudC5jaGFyID0gIiIpCmZwa20ubWF0cml4LmFubm88LWZwa20ubWF0cml4WywxOjZdCmZwa20ubWF0cml4PC1mcGttLm1hdHJpeFssNzpuY29sKGZwa20ubWF0cml4KV0KICAKIyBsb2FkIGluIHRoZSBjbGluaWNhbCBkYXRhIGFuZCBzdWJzZXQgdGhlIHNhbXBsZXMgdG8gdGhvc2UgbGlzdGVkIGluIHRoZSBmcGttLm1hdHJpeApjbGluaWMubWF0cml4PXJlYWQudGFibGUoY2xpbmljLmZpbGUsc2VwPSJcdCIsaGVhZGVyPVQsY2hlY2submFtZXM9RixzdHJpbmdzQXNGYWN0b3JzPUYpCnJvd25hbWVzKGNsaW5pYy5tYXRyaXgpPC1hcy5tYXRyaXgoY2xpbmljLm1hdHJpeClbLDFdICMgR1JBRFMgSUQKY2xpbmljLm1hdHJpeDwtY2xpbmljLm1hdHJpeFtjb2xuYW1lcyhmcGttLm1hdHJpeCksXQoKIyBiYXNlZCBvbiB2YXIubmFtZSwgZ3JvdXAxLnZhbHVlcywgYW5kIGdyb3VwMi52YWx1ZXMsIGlkZW50aWZ5IHRoZSBzYW1wbGVzIHRvIGluY2x1ZGUgaW4gdGhlIGZpbGVzLgppZihmaWxlLmV4aXN0cyhvdXRwdXQuZm9sZGVyKT09Ril7CiAgZGlyLmNyZWF0ZShvdXRwdXQuZm9sZGVyKQp9CiAgCmdjdC5maWxlcGF0aDwtZmlsZS5wYXRoKG91dHB1dC5mb2xkZXIscGFzdGUoImdleHBfIixzdWZmaXgubmFtZSwiLmdjdCIsc2VwPSIiKSkKY2xzLmZpbGVwYXRoPC1maWxlLnBhdGgob3V0cHV0LmZvbGRlcixwYXN0ZSgiY2xzXyIsc3VmZml4Lm5hbWUsIi5jbHMiLHNlcD0iIikpCiAgCiMgc3Vic3RyYWN0IHNhbXBsZXMgd2l0aCB2YXIubmFtZSBlcXVhbCB0byB0aGUgdmFsdWVzIGluIGdyb3VwMS52YWx1ZXMgYW5kIGdyb3VwMi4gdmFsdWVzCiN0ZW1wMTwtZnBrbS5tYXRyaXhbLGFwcGx5KGFzLmRhdGEuZnJhbWUoY2xpbmljLm1hdHJpeFssdmFyLm5hbWVzXSksMSxwYXN0ZSxjb2xsYXBzZT0iXyIpJWluJWFwcGx5KGdyb3VwMS52YWx1ZXMsMSxwYXN0ZSxjb2xsYXBzZT0iXyIpXQp0ZW1wMTwtZnBrbS5tYXRyaXhbLGx5bXBob25seS5pZF0KdGVtcDI8LWZwa20ubWF0cml4WyxhcHBseShhcy5kYXRhLmZyYW1lKGNsaW5pYy5tYXRyaXhbLHZhci5uYW1lc10pLDEscGFzdGUsY29sbGFwc2U9Il8iKSVpbiVhcHBseShncm91cDIudmFsdWVzLDEscGFzdGUsY29sbGFwc2U9Il8iKV0KCmNhdCgiVGhlcmUgYXJlICIsbmNvbCh0ZW1wMSksIiBhbmQgIiwgbmNvbCh0ZW1wMiksIiBzYW1wbGVzIGZvciBncm91cDEgYW5kIGdyb3VwMiwgcmVzcGVjdGl2ZWx5LlxuIikKZGF0YS5tYXRyaXg8LWNiaW5kKHRlbXAxLHRlbXAyKQpkYXRhLm1hdHJpeDwtY2JpbmQoZnBrbS5tYXRyaXguYW5ub1ssMV0scmVwKCJuYSIsbnJvdyhkYXRhLm1hdHJpeCkpLGRhdGEubWF0cml4KQpjb2xuYW1lcyhkYXRhLm1hdHJpeClbMToyXTwtYygiTkFNRSIsIkRlc2NyaXB0aW9uIikKcGhlbm8udmVjdDwtYyhyZXAoMCxuY29sKHRlbXAxKSkscmVwKDEsbmNvbCh0ZW1wMikpKQoKIyBvdXRwdXQgdGhlIGdlbmUgZXhwcmVzc2lvbiBkYXRhCmNtZC5vdXQ8LSIjMS4yXG4iCmNtZC5vdXQ8LXBhc3RlKGNtZC5vdXQsbnJvdyhkYXRhLm1hdHJpeCksIlx0IixuY29sKGRhdGEubWF0cml4KS0yLCJcbiIsc2VwPSIiKQpjYXQoY21kLm91dCxmaWxlPWdjdC5maWxlcGF0aCxhcHBlbmQ9RikKd3JpdGUudGFibGUoZGF0YS5tYXRyaXgsc2VwPSJcdCIsZmlsZT1nY3QuZmlsZXBhdGgsYXBwZW5kPVQscm93Lm5hbWVzPUYsY29sLm5hbWVzPVQscXVvdGU9RikKCiMgb3V0cHV0IHRoZSBwaGVub3R5cGUgZmlsZQpjbWQub3V0PC1wYXN0ZShuY29sKGRhdGEubWF0cml4KS0yLCIgIiwyLCIgIiwxLCJcbiIsc2VwPSIiKQpjbWQub3V0PC1wYXN0ZShjbWQub3V0LCIjIGdyb3VwMSBncm91cDJcbiIsc2VwPSIiKQpjYXQoY21kLm91dCxmaWxlPWNscy5maWxlcGF0aCxhcHBlbmQ9RixzZXA9IiIpCmNhdChwaGVuby52ZWN0LGZpbGU9Y2xzLmZpbGVwYXRoLGFwcGVuZD1ULHNlcD0iICIpCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCmBgYAoKV2UgZXhhbWluZSB0aGUgQ1QgZmVhdHVyZXMgb2YgdGhlc2UgdGhyZWUgZ3JvdXBzIG9mIHBhdGllbnRzLgpgYGB7cn0KIyBsb2FkIGluIHRoZSBsaXN0IG9mIEdSQURTIElEcyBpbiBhbGwgdGhyZWUgZ3JvdXBzCmdyYWRzLmlkLmxpc3Q8LWZpbGUucGF0aChob21lLmRpciwic2NyYXRjaC9HUkFEUy9TQVJDX3Jlc3VsdHMvUmVzdWx0c19zdW1tYXJ5X1BCTUNfaGczOC9iYXNlbGluZS9HU0VBX2tub3duZ2VuZXNfREVTZXEyX2xvZzJhZGp1c3RlZC9ncmFkc2lkX292ZXJsYXBfbm9ub3ZlcmxhcC54bHN4IikKZ3JhZHMuaWQubWF0cml4PC1yZWFkLnhscyhncmFkcy5pZC5saXN0LHNoZWV0PTEpCm92ZXJsYXAuaWQ8LWFzLmNoYXJhY3RlcihncmFkcy5pZC5tYXRyaXhbLDFdKVthcy5jaGFyYWN0ZXIoZ3JhZHMuaWQubWF0cml4WywxXSkhPSIiXQpzdGFnZTFvbmx5LmlkPC1hcy5jaGFyYWN0ZXIoZ3JhZHMuaWQubWF0cml4WywyXSlbYXMuY2hhcmFjdGVyKGdyYWRzLmlkLm1hdHJpeFssMl0pIT0iIl0KbHltcGhvbmx5LmlkPC1hcy5jaGFyYWN0ZXIoZ3JhZHMuaWQubWF0cml4WywzXSlbYXMuY2hhcmFjdGVyKGdyYWRzLmlkLm1hdHJpeFssM10pIT0iIl0KCiMgbG9hZCBpbiB0aGUgY2xpbmljYWwgZGF0YQpjbGluaWMuZmlsZXBhdGg8LWZpbGUucGF0aChob21lLmRpciwic2NyYXRjaC9HUkFEUy9TQVJDX3Jlc3VsdHMvUmVzdWx0c19zdW1tYXJ5X1BCTUNfaGczOC9iYXNlbGluZS9kYXRhL2NsaW5pY19tYXRyaXhfbWVyZ2VkLlJEUyIpCmNsaW5pYy5tYXRyaXg8LXJlYWRSRFMoY2xpbmljLmZpbGVwYXRoLHJlZmhvb2sgPSBOVUxMKQpjbGluaWMubWF0cml4PC1jbGluaWMubWF0cml4WyxjKCJHRU5ERVIiLCJSQUNFIiwiZXRobiIsIkFHRSIsImV0aG9yIiwid2JjIiwiY2Q0IiwiY2FsIiwiZDI1IiwiZDEyNSIsImNycCIsInBfbHltcGgiLCJwX21vbm8iLCJwX25ldXQiLCJwX2VvcyIsInBfYmFzbyIsIkZWQ1BSRUQiLCJGRVYxUFJFRCIsIlBSRURETENPIiwiU0NBRERJTkciLCJzbW9rZSIsInBrX3lyIiwic3Rlcm9pZF9hdHYxIiwiZG1hcmRfYXR2MSIsIk1pY3Jvbm9kdWxlIiwiTWVkX0x5bXBoYWRlbm9wYXRoeSIsIkhpbGFyX0x5bXBoYWRlbm9wYXRoeSIsIm5vZHVsZV9seW1waF9waGVubyIpXQpjbGluaWMubWF0cml4PC1jbGluaWMubWF0cml4W2Mob3ZlcmxhcC5pZCxzdGFnZTFvbmx5LmlkLGx5bXBob25seS5pZCksXQoKIyBnZW5lcmF0ZSB0aGUgc3VtbWFyeSB0YWJsZQoKdmFyLm5hbWVzPC1jKCJHRU5ERVIiLCJSQUNFIiwiQUdFIiwiZXRob3IiLCJ3YmMiLCJjZDQiLCJjYWwiLCJkMjUiLCJkMTI1IiwiY3JwIiwicF9seW1waCIsInBfbW9ubyIsInBfbmV1dCIsInBfZW9zIiwicF9iYXNvIiwiRlZDUFJFRCIsIkZFVjFQUkVEIiwiUFJFRERMQ08iLCJTQ0FERElORyIsInNtb2tlIiwicGtfeXIiLCJzdGVyb2lkX2F0djEiLCJkbWFyZF9hdHYxIiwiTWljcm9ub2R1bGUiLCJNZWRfTHltcGhhZGVub3BhdGh5IiwiSGlsYXJfTHltcGhhZGVub3BhdGh5Iiwibm9kdWxlX2x5bXBoX3BoZW5vIikKCnZhci5uYW1lcy5jYXQ8LWMoIkdFTkRFUiIsIlJBQ0UiLCJldGhvciIsInNtb2tlIiwic3Rlcm9pZF9hdHYxIiwiZG1hcmRfYXR2MSIsIlNDQURESU5HIiwiTWljcm9ub2R1bGUiLCJNZWRfTHltcGhhZGVub3BhdGh5IiwiSGlsYXJfTHltcGhhZGVub3BhdGh5Iiwibm9kdWxlX2x5bXBoX3BoZW5vIikKdmFyLm5hbWVzLmNvbjwtYygiQUdFIiwid2JjIiwiY2Q0IiwiY2FsIiwiZDI1IiwiZDEyNSIsImNycCIsInBfbHltcGgiLCJwX21vbm8iLCJwX25ldXQiLCJwX2VvcyIsInBfYmFzbyIsIkZWQ1BSRUQiLCJGRVYxUFJFRCIsIlBSRURETENPIiwicGtfeXIiKQoKdmFyLnR5cGU8LXJlcCgiY29uIixsZW5ndGgodmFyLm5hbWVzKSkKdmFyLnR5cGVbdmFyLm5hbWVzJWluJXZhci5uYW1lcy5jYXRdPC0iY2F0Igp2YXIuYW5ub3Q8LXZhci5uYW1lcwoKc3VtbWFyeS50YWJsZTwtY2hhcmFjdGVyKCkKCmZvcihpIGluIDE6bGVuZ3RoKHZhci5uYW1lcykpewogIGlmKHZhci50eXBlW2ldPT0iY2F0Iil7CiAgICB0ZW1wLm1hdHJpeDwtYXMubWF0cml4KHRhYmxlKGNsaW5pYy5tYXRyaXhbb3ZlcmxhcC5pZCx2YXIubmFtZXNbaV1dKSkKICAgIHJvd25hbWVzKHRlbXAubWF0cml4KTwtcGFzdGUodmFyLmFubm90W2ldLCIgLSAiLHJvd25hbWVzKHRlbXAubWF0cml4KSkKICAgIAogICAgdGVtcC5zdW08LXN1bSh0ZW1wLm1hdHJpeCkKICAgIHRlbXAubWF0cml4MjwtdGVtcC5tYXRyaXgvdGVtcC5zdW0KICAgIHN1bW1hcnkudGFibGU8LXJiaW5kKHN1bW1hcnkudGFibGUsYXMubWF0cml4KHBhc3RlKHRlbXAubWF0cml4LCIoIixyb3VuZCh0ZW1wLm1hdHJpeDIsZGlnaXRzPTIpLCIpIixzZXA9IiIpKSkKICAgIHJvd25hbWVzKHN1bW1hcnkudGFibGUpWyhucm93KHN1bW1hcnkudGFibGUpLW5yb3codGVtcC5tYXRyaXgpKzEpOm5yb3coc3VtbWFyeS50YWJsZSldPC1yb3duYW1lcyh0ZW1wLm1hdHJpeCkKICAgIH1lbHNlewogICAgdGVtcC5tZWFuPC1tZWFuKGNsaW5pYy5tYXRyaXhbb3ZlcmxhcC5pZCx2YXIubmFtZXNbaV1dLG5hLnJtPVQpCiAgICB0ZW1wLnNkPC1zZChjbGluaWMubWF0cml4W292ZXJsYXAuaWQsdmFyLm5hbWVzW2ldXSxuYS5ybT1UKQogICAgdGVtcC5tZWFuPC1yb3VuZCh0ZW1wLm1lYW4sZGlnaXRzPTIpCiAgICB0ZW1wLnNkPC1yb3VuZCh0ZW1wLnNkLGRpZ2l0cz0yKQogICAgc3VtbWFyeS50YWJsZTwtcmJpbmQoc3VtbWFyeS50YWJsZSxwYXN0ZSh0ZW1wLm1lYW4sIsKxIix0ZW1wLnNkLHNlcD0iIikpCiAgICByb3duYW1lcyhzdW1tYXJ5LnRhYmxlKVtucm93KHN1bW1hcnkudGFibGUpXTwtcGFzdGUodmFyLmFubm90W2ldLCIsIG1lYW7CsVNEIixzZXA9IiIpCiAgfQogIAp9Cgpjb2xuYW1lcyhzdW1tYXJ5LnRhYmxlKVsxXTwtIk92ZXJsYXAiCnN1bW1hcnkudGFibGUudG90YWw8LXN1bW1hcnkudGFibGUKCgpzdW1tYXJ5LnRhYmxlPC1jaGFyYWN0ZXIoKQoKZm9yKGkgaW4gMTpsZW5ndGgodmFyLm5hbWVzKSl7CiAgaWYodmFyLnR5cGVbaV09PSJjYXQiKXsKICAgIHRlbXAubWF0cml4PC1hcy5tYXRyaXgodGFibGUoY2xpbmljLm1hdHJpeFtzdGFnZTFvbmx5LmlkLHZhci5uYW1lc1tpXV0pKQogICAgcm93bmFtZXModGVtcC5tYXRyaXgpPC1wYXN0ZSh2YXIuYW5ub3RbaV0sIiAtICIscm93bmFtZXModGVtcC5tYXRyaXgpKQogICAgCiAgICB0ZW1wLnN1bTwtc3VtKHRlbXAubWF0cml4KQogICAgdGVtcC5tYXRyaXgyPC10ZW1wLm1hdHJpeC90ZW1wLnN1bQogICAgc3VtbWFyeS50YWJsZTwtcmJpbmQoc3VtbWFyeS50YWJsZSxhcy5tYXRyaXgocGFzdGUodGVtcC5tYXRyaXgsIigiLHJvdW5kKHRlbXAubWF0cml4MixkaWdpdHM9MiksIikiLHNlcD0iIikpKQogICAgcm93bmFtZXMoc3VtbWFyeS50YWJsZSlbKG5yb3coc3VtbWFyeS50YWJsZSktbnJvdyh0ZW1wLm1hdHJpeCkrMSk6bnJvdyhzdW1tYXJ5LnRhYmxlKV08LXJvd25hbWVzKHRlbXAubWF0cml4KQogICAgfWVsc2V7CiAgICB0ZW1wLm1lYW48LW1lYW4oY2xpbmljLm1hdHJpeFtzdGFnZTFvbmx5LmlkLHZhci5uYW1lc1tpXV0sbmEucm09VCkKICAgIHRlbXAuc2Q8LXNkKGNsaW5pYy5tYXRyaXhbc3RhZ2Uxb25seS5pZCx2YXIubmFtZXNbaV1dLG5hLnJtPVQpCiAgICB0ZW1wLm1lYW48LXJvdW5kKHRlbXAubWVhbixkaWdpdHM9MikKICAgIHRlbXAuc2Q8LXJvdW5kKHRlbXAuc2QsZGlnaXRzPTIpCiAgICBzdW1tYXJ5LnRhYmxlPC1yYmluZChzdW1tYXJ5LnRhYmxlLHBhc3RlKHRlbXAubWVhbiwiwrEiLHRlbXAuc2Qsc2VwPSIiKSkKICAgIHJvd25hbWVzKHN1bW1hcnkudGFibGUpW25yb3coc3VtbWFyeS50YWJsZSldPC1wYXN0ZSh2YXIuYW5ub3RbaV0sIiwgbWVhbsKxU0QiLHNlcD0iIikKICB9CiAgCn0KCmNvbG5hbWVzKHN1bW1hcnkudGFibGUpWzFdPC0iU3RhZ2UgSSBvbmx5IgoKdGVtcC5uYW1lczwtdW5pb24ocm93bmFtZXMoc3VtbWFyeS50YWJsZS50b3RhbCkscm93bmFtZXMoc3VtbWFyeS50YWJsZSkpCnRlbXA8LW1hdHJpeCgiIixucm93PWxlbmd0aCh0ZW1wLm5hbWVzKSxuY29sPTIpCnJvd25hbWVzKHRlbXApPC10ZW1wLm5hbWVzCnRlbXBbcm93bmFtZXMoc3VtbWFyeS50YWJsZS50b3RhbCksMV08LXN1bW1hcnkudGFibGUudG90YWxbLDFdCnRlbXBbcm93bmFtZXMoc3VtbWFyeS50YWJsZSksMl08LXN1bW1hcnkudGFibGVbLDFdCnN1bW1hcnkudGFibGUudG90YWw8LXRlbXAKCgpzdW1tYXJ5LnRhYmxlPC1jaGFyYWN0ZXIoKQoKZm9yKGkgaW4gMTpsZW5ndGgodmFyLm5hbWVzKSl7CiAgaWYodmFyLnR5cGVbaV09PSJjYXQiKXsKICAgIHRlbXAubWF0cml4PC1hcy5tYXRyaXgodGFibGUoY2xpbmljLm1hdHJpeFtseW1waG9ubHkuaWQsdmFyLm5hbWVzW2ldXSkpCiAgICByb3duYW1lcyh0ZW1wLm1hdHJpeCk8LXBhc3RlKHZhci5hbm5vdFtpXSwiIC0gIixyb3duYW1lcyh0ZW1wLm1hdHJpeCkpCiAgICAKICAgIHRlbXAuc3VtPC1zdW0odGVtcC5tYXRyaXgpCiAgICB0ZW1wLm1hdHJpeDI8LXRlbXAubWF0cml4L3RlbXAuc3VtCiAgICBzdW1tYXJ5LnRhYmxlPC1yYmluZChzdW1tYXJ5LnRhYmxlLGFzLm1hdHJpeChwYXN0ZSh0ZW1wLm1hdHJpeCwiKCIscm91bmQodGVtcC5tYXRyaXgyLGRpZ2l0cz0yKSwiKSIsc2VwPSIiKSkpCiAgICByb3duYW1lcyhzdW1tYXJ5LnRhYmxlKVsobnJvdyhzdW1tYXJ5LnRhYmxlKS1ucm93KHRlbXAubWF0cml4KSsxKTpucm93KHN1bW1hcnkudGFibGUpXTwtcm93bmFtZXModGVtcC5tYXRyaXgpCiAgICB9ZWxzZXsKICAgIHRlbXAubWVhbjwtbWVhbihjbGluaWMubWF0cml4W2x5bXBob25seS5pZCx2YXIubmFtZXNbaV1dLG5hLnJtPVQpCiAgICB0ZW1wLnNkPC1zZChjbGluaWMubWF0cml4W2x5bXBob25seS5pZCx2YXIubmFtZXNbaV1dLG5hLnJtPVQpCiAgICB0ZW1wLm1lYW48LXJvdW5kKHRlbXAubWVhbixkaWdpdHM9MikKICAgIHRlbXAuc2Q8LXJvdW5kKHRlbXAuc2QsZGlnaXRzPTIpCiAgICBzdW1tYXJ5LnRhYmxlPC1yYmluZChzdW1tYXJ5LnRhYmxlLHBhc3RlKHRlbXAubWVhbiwiwrEiLHRlbXAuc2Qsc2VwPSIiKSkKICAgIHJvd25hbWVzKHN1bW1hcnkudGFibGUpW25yb3coc3VtbWFyeS50YWJsZSldPC1wYXN0ZSh2YXIuYW5ub3RbaV0sIiwgbWVhbsKxU0QiLHNlcD0iIikKICB9CiAgCn0KCmNvbG5hbWVzKHN1bW1hcnkudGFibGUpWzFdPC0iTHltcGggb25seSIKCnRlbXAubmFtZXM8LXVuaW9uKHJvd25hbWVzKHN1bW1hcnkudGFibGUudG90YWwpLHJvd25hbWVzKHN1bW1hcnkudGFibGUpKQp0ZW1wPC1tYXRyaXgoIiIsbnJvdz1sZW5ndGgodGVtcC5uYW1lcyksbmNvbD0zKQpyb3duYW1lcyh0ZW1wKTwtdGVtcC5uYW1lcwp0ZW1wW3Jvd25hbWVzKHN1bW1hcnkudGFibGUudG90YWwpLDFdPC1zdW1tYXJ5LnRhYmxlLnRvdGFsWywxXQp0ZW1wW3Jvd25hbWVzKHN1bW1hcnkudGFibGUudG90YWwpLDJdPC1zdW1tYXJ5LnRhYmxlLnRvdGFsWywyXQp0ZW1wW3Jvd25hbWVzKHN1bW1hcnkudGFibGUpLDNdPC1zdW1tYXJ5LnRhYmxlWywxXQoKc3VtbWFyeS50YWJsZS50b3RhbDwtdGVtcAoKY29sbmFtZXMoc3VtbWFyeS50YWJsZS50b3RhbCk8LWMoIm92ZXJsYXAiLCJTdGFnZSAxIG9ubHkiLCJMeW1waCBvbmx5IikKCgojIGNhbGN1bGF0ZSB0aGUgcCB2YWx1ZXMgdG8gYXNzZXNzIHRoZSBkaWZmZXJlbmNlcyBiZXR3ZWVuIHRoZSB0aHJlZSBncm91cHMgb2YgcGF0aWVudHMgZm9yIGVhY2ggdHJhaXQKCnB2YWx1ZS52ZWN0PC1udW1lcmljKCkKCmZvcihpIGluIDE6bGVuZ3RoKHZhci5uYW1lcykpewogIAogIGlmKHZhci50eXBlW2ldPT0iY2F0Iil7CiAgICB0ZW1wLm1hdHJpeDwtYXMubWF0cml4KHRhYmxlKGNsaW5pYy5tYXRyaXhbYyhvdmVybGFwLmlkLHN0YWdlMW9ubHkuaWQsbHltcGhvbmx5LmlkKSx2YXIubmFtZXNbaV1dKSkKICAgIHJvd25hbWVzKHRlbXAubWF0cml4KTwtcGFzdGUodmFyLmFubm90W2ldLCIgLSAiLHJvd25hbWVzKHRlbXAubWF0cml4KSkKICAgIAogICAgdGVtcDE8LWNsaW5pYy5tYXRyaXhbYyhvdmVybGFwLmlkLHN0YWdlMW9ubHkuaWQsbHltcGhvbmx5LmlkKSx2YXIubmFtZXNbaV1dCiAgICB0ZW1wMjwtYyhyZXAoIm92ZXJsYXAiLGxlbmd0aChvdmVybGFwLmlkKSkscmVwKCJzdGFnZTEiLGxlbmd0aChzdGFnZTFvbmx5LmlkKSkscmVwKCJseW1waCIsbGVuZ3RoKGx5bXBob25seS5pZCkpKQogICAgdGVtcC5pbmRleDwtMTpsZW5ndGgodGVtcDEpCiAgICB0ZW1wLmluZGV4PC10ZW1wLmluZGV4WyEoaXMubmEodGVtcDEpIHwgaXMubmEodGVtcDIpKV0KICAgIHRlbXAxPC10ZW1wMVt0ZW1wLmluZGV4XQogICAgdGVtcDI8LXRlbXAyW3RlbXAuaW5kZXhdCiAgICAKICAgIGlmKGxlbmd0aCh1bmlxdWUodGVtcDEpKT09MSl7CiAgICAgIHB2YWx1ZS52ZWN0PC1jKHB2YWx1ZS52ZWN0LCByZXAoMSxsZW5ndGgodW5pcXVlKHRlbXAxKSkpKQogICAgICBuYW1lcyhwdmFsdWUudmVjdClbbGVuZ3RoKHB2YWx1ZS52ZWN0KV08LXJvd25hbWVzKHRlbXAubWF0cml4KQogICAgfWVsc2V7CiAgICAgIHB2YWx1ZS52ZWN0PC1jKHB2YWx1ZS52ZWN0LCByZXAoY2hpc3EudGVzdCh0ZW1wMSxhcy5mYWN0b3IodGVtcDIpKSRwLnZhbHVlLGxlbmd0aCh1bmlxdWUodGVtcDEpKSkpICAKICAgICAgbmFtZXMocHZhbHVlLnZlY3QpWyhsZW5ndGgocHZhbHVlLnZlY3QpLWxlbmd0aCh1bmlxdWUodGVtcDEpKSsxKTpsZW5ndGgocHZhbHVlLnZlY3QpXTwtcm93bmFtZXModGVtcC5tYXRyaXgpCiAgICB9CiAgICAKICAgIAogICAgfWVsc2V7CiAgICB0ZW1wMTwtY2xpbmljLm1hdHJpeFtjKG92ZXJsYXAuaWQsc3RhZ2Uxb25seS5pZCxseW1waG9ubHkuaWQpLHZhci5uYW1lc1tpXV0KICAgIHRlbXAyPC1jKHJlcCgib3ZlcmxhcCIsbGVuZ3RoKG92ZXJsYXAuaWQpKSxyZXAoInN0YWdlMSIsbGVuZ3RoKHN0YWdlMW9ubHkuaWQpKSxyZXAoImx5bXBoIixsZW5ndGgobHltcGhvbmx5LmlkKSkpCiAgICBwdmFsdWUudmVjdDwtYyhwdmFsdWUudmVjdCwga3J1c2thbC50ZXN0KHRlbXAxfmFzLmZhY3Rvcih0ZW1wMikpJHAudmFsdWUpCiAgICBuYW1lcyhwdmFsdWUudmVjdClbbGVuZ3RoKHB2YWx1ZS52ZWN0KV08LXBhc3RlKHZhci5hbm5vdFtpXSwiLCBtZWFuwrFTRCIsc2VwPSIiKQogIH0KICAKfQoKc3VtbWFyeS50YWJsZS50b3RhbDwtY2JpbmQoc3VtbWFyeS50YWJsZS50b3RhbCxwdmFsdWUudmVjdFtyb3duYW1lcyhzdW1tYXJ5LnRhYmxlLnRvdGFsKV0pCmNvbG5hbWVzKHN1bW1hcnkudGFibGUudG90YWwpWzRdPC0icCB2YWx1ZSIKCm91dHB1dC5maWxlcGF0aDwtZmlsZS5wYXRoKGhvbWUuZGlyLCJzY3JhdGNoL0dSQURTL1NBUkNfcmVzdWx0cy9SZXN1bHRzX3N1bW1hcnlfUEJNQ19oZzM4L2Jhc2VsaW5lL0dTRUFfa25vd25nZW5lc19ERVNlcTJfbG9nMmFkanVzdGVkL2dyYWRzaWRfb3ZlcmxhcF9ub25vdmVybGFwX2NsaW5pY2ZlYXR1cmVzLnhsc3giKQp3cml0ZS54bHN4KHN1bW1hcnkudGFibGUudG90YWwsZmlsZT1vdXRwdXQuZmlsZXBhdGgscm93Lm5hbWVzPVQsY29sLm5hbWVzPVQsYXBwZW5kPUYpCgpgYGAKCldlIGFsc28gZ2VuZXJhdGUgdGhlIGZlYXR1cmUgdGFibGUgZm9yIHRoZSBDVCBzY2FuIHZhcmlhYmxlcy4KCmBgYHtyfQojIGxvYWQgaW4gdGhlIGxpc3Qgb2YgR1JBRFMgSURzIGluIGFsbCB0aHJlZSBncm91cHMKZ3JhZHMuaWQubGlzdDwtZmlsZS5wYXRoKGhvbWUuZGlyLCJzY3JhdGNoL0dSQURTL1NBUkNfcmVzdWx0cy9SZXN1bHRzX3N1bW1hcnlfUEJNQ19oZzM4L2Jhc2VsaW5lL0dTRUFfa25vd25nZW5lc19ERVNlcTJfbG9nMmFkanVzdGVkL2dyYWRzaWRfb3ZlcmxhcF9ub25vdmVybGFwLnhsc3giKQpncmFkcy5pZC5tYXRyaXg8LXJlYWQueGxzKGdyYWRzLmlkLmxpc3Qsc2hlZXQ9MSkKb3ZlcmxhcC5pZDwtYXMuY2hhcmFjdGVyKGdyYWRzLmlkLm1hdHJpeFssMV0pW2FzLmNoYXJhY3RlcihncmFkcy5pZC5tYXRyaXhbLDFdKSE9IiJdCnN0YWdlMW9ubHkuaWQ8LWFzLmNoYXJhY3RlcihncmFkcy5pZC5tYXRyaXhbLDJdKVthcy5jaGFyYWN0ZXIoZ3JhZHMuaWQubWF0cml4WywyXSkhPSIiXQpseW1waG9ubHkuaWQ8LWFzLmNoYXJhY3RlcihncmFkcy5pZC5tYXRyaXhbLDNdKVthcy5jaGFyYWN0ZXIoZ3JhZHMuaWQubWF0cml4WywzXSkhPSIiXQoKIyBsb2FkIGluIHRoZSBjbGluaWNhbCBkYXRhCmN0LmZpbGVwYXRoPC1maWxlLnBhdGgoaG9tZS5kaXIsInNjcmF0Y2gvR1JBRFMvU0FSQ19yZXN1bHRzL0RhdGEvQ1RfZGF0YV8yMDE3MDcxMi9TYXJjX2N0X3JlYWRzX2NvcnJlY3RlZC54bHMiKQpjdC5tYXRyaXg8LXJlYWQueGxzKGN0LmZpbGVwYXRoLHNoZWV0PTEpCnJvd25hbWVzKGN0Lm1hdHJpeCk8LWFzLm1hdHJpeChjdC5tYXRyaXgpWywiR1JBRFNJRCJdCmN0Lm1hdHJpeDwtY3QubWF0cml4WyxjKCJNZWRfTHltcGhhZGVub3BhdGh5IiwiSGlsYXJfTHltcGhhZGVub3BhdGh5IiwiTWljcm9ub2R1bGUiLCJCcm9uY2hpYWxfV2FsbF9UaGlja2VuaW5nIiwiVHJhY3Rpb25fQnJvbmNoaWVjdGFzaXMiLCJCcm9uY2hpZWN0YXNpc19zZXZlcml0eSIsIkdyb3VuZF9HbGFzcyIsIkhvbmV5Y29tYmluZyIsIlJldGljdWxhcl9BYm5vcm1hbGl0eSIsIlB1bG1vbmFyeV9BcnQiLCJUcmVlX2luX2J1ZCIpXQoKY3QubWF0cml4PC1jdC5tYXRyaXhbYyhvdmVybGFwLmlkLHN0YWdlMW9ubHkuaWQsbHltcGhvbmx5LmlkKSxdCmdyb3VwLnZlY3Q8LWMocmVwKCJvdmVybGFwIixsZW5ndGgob3ZlcmxhcC5pZCkpLHJlcCgiU3RhZ2UgSSIsbGVuZ3RoKHN0YWdlMW9ubHkuaWQpKSxyZXAoIkx5bXBoIixsZW5ndGgobHltcGhvbmx5LmlkKSkpCgojIHJlY29kZSBNZWRfTHltcGhhZGVub3BhdGh5IGFuZCBIaWxhcl9MeW1waGFkZW5vcGF0aHkKdGVtcDwtY3QubWF0cml4WywiTWVkX0x5bXBoYWRlbm9wYXRoeSJdCnRlbXAxPC1yZXAoMCxsZW5ndGgodGVtcCkpCnRlbXAxW3RlbXAlaW4lYygxLDIpXTwtMQp0ZW1wMVt0ZW1wPT0zXTwtMiAgICMgMD1ubywgMT1vbmUgc2lkZSwgMj1iaWxsYXRlcmFsCmN0Lm1hdHJpeFssIk1lZF9MeW1waGFkZW5vcGF0aHkiXTwtdGVtcDEKCgp0ZW1wPC1jdC5tYXRyaXhbLCJIaWxhcl9MeW1waGFkZW5vcGF0aHkiXQp0ZW1wMTwtcmVwKDAsbGVuZ3RoKHRlbXApKQp0ZW1wMVt0ZW1wJWluJWMoMSwyKV08LTEKdGVtcDFbdGVtcD09M108LTIgICAjIDA9bm8sIDE9b25lIHNpZGUsIDI9YmlsbGF0ZXJhbApjdC5tYXRyaXhbLCJIaWxhcl9MeW1waGFkZW5vcGF0aHkiXTwtdGVtcDEKCgojIGdlbmVyYXRlIHRoZSBzdW1tYXJ5IHRhYmxlCnZhci5uYW1lczwtYygiTWVkX0x5bXBoYWRlbm9wYXRoeSIsIkhpbGFyX0x5bXBoYWRlbm9wYXRoeSIsIk1pY3Jvbm9kdWxlIiwiQnJvbmNoaWFsX1dhbGxfVGhpY2tlbmluZyIsIlRyYWN0aW9uX0Jyb25jaGllY3Rhc2lzIiwiQnJvbmNoaWVjdGFzaXNfc2V2ZXJpdHkiLCJHcm91bmRfR2xhc3MiLCJIb25leWNvbWJpbmciLCJSZXRpY3VsYXJfQWJub3JtYWxpdHkiLCJQdWxtb25hcnlfQXJ0IiwiVHJlZV9pbl9idWQiKQoKc3VtbWFyeS50YWJsZTwtY2hhcmFjdGVyKCkKCmZvcihpIGluIDE6bGVuZ3RoKHZhci5uYW1lcykpewogIAogICAgdGVtcC5tYXRyaXg8LWFzLm1hdHJpeCh0YWJsZShjdC5tYXRyaXhbb3ZlcmxhcC5pZCx2YXIubmFtZXNbaV1dKSkKICAgIHJvd25hbWVzKHRlbXAubWF0cml4KTwtcGFzdGUodmFyLm5hbWVzW2ldLCIgLSAiLHJvd25hbWVzKHRlbXAubWF0cml4KSkKICAgIAogICAgdGVtcC5zdW08LXN1bSh0ZW1wLm1hdHJpeCkKICAgIHRlbXAubWF0cml4MjwtdGVtcC5tYXRyaXgvdGVtcC5zdW0KICAgIHN1bW1hcnkudGFibGU8LXJiaW5kKHN1bW1hcnkudGFibGUsYXMubWF0cml4KHBhc3RlKHRlbXAubWF0cml4LCIoIixyb3VuZCh0ZW1wLm1hdHJpeDIsZGlnaXRzPTIpLCIpIixzZXA9IiIpKSkKICAgIHJvd25hbWVzKHN1bW1hcnkudGFibGUpWyhucm93KHN1bW1hcnkudGFibGUpLW5yb3codGVtcC5tYXRyaXgpKzEpOm5yb3coc3VtbWFyeS50YWJsZSldPC1yb3duYW1lcyh0ZW1wLm1hdHJpeCkKfQoKY29sbmFtZXMoc3VtbWFyeS50YWJsZSlbMV08LSJPdmVybGFwIgpzdW1tYXJ5LnRhYmxlLnRvdGFsPC1zdW1tYXJ5LnRhYmxlCgpzdW1tYXJ5LnRhYmxlPC1jaGFyYWN0ZXIoKQoKZm9yKGkgaW4gMTpsZW5ndGgodmFyLm5hbWVzKSl7CiAgCiAgICB0ZW1wLm1hdHJpeDwtYXMubWF0cml4KHRhYmxlKGN0Lm1hdHJpeFtzdGFnZTFvbmx5LmlkLHZhci5uYW1lc1tpXV0pKQogICAgcm93bmFtZXModGVtcC5tYXRyaXgpPC1wYXN0ZSh2YXIubmFtZXNbaV0sIiAtICIscm93bmFtZXModGVtcC5tYXRyaXgpKQogICAgCiAgICB0ZW1wLnN1bTwtc3VtKHRlbXAubWF0cml4KQogICAgdGVtcC5tYXRyaXgyPC10ZW1wLm1hdHJpeC90ZW1wLnN1bQogICAgc3VtbWFyeS50YWJsZTwtcmJpbmQoc3VtbWFyeS50YWJsZSxhcy5tYXRyaXgocGFzdGUodGVtcC5tYXRyaXgsIigiLHJvdW5kKHRlbXAubWF0cml4MixkaWdpdHM9MiksIikiLHNlcD0iIikpKQogICAgcm93bmFtZXMoc3VtbWFyeS50YWJsZSlbKG5yb3coc3VtbWFyeS50YWJsZSktbnJvdyh0ZW1wLm1hdHJpeCkrMSk6bnJvdyhzdW1tYXJ5LnRhYmxlKV08LXJvd25hbWVzKHRlbXAubWF0cml4KQp9Cgpjb2xuYW1lcyhzdW1tYXJ5LnRhYmxlKVsxXTwtIlN0YWdlIEkiCgoKdGVtcC5uYW1lczwtdW5pb24ocm93bmFtZXMoc3VtbWFyeS50YWJsZS50b3RhbCkscm93bmFtZXMoc3VtbWFyeS50YWJsZSkpCnRlbXA8LW1hdHJpeCgiIixucm93PWxlbmd0aCh0ZW1wLm5hbWVzKSxuY29sPTIpCnJvd25hbWVzKHRlbXApPC10ZW1wLm5hbWVzCnRlbXBbcm93bmFtZXMoc3VtbWFyeS50YWJsZS50b3RhbCksMV08LXN1bW1hcnkudGFibGUudG90YWxbLDFdCnRlbXBbcm93bmFtZXMoc3VtbWFyeS50YWJsZSksMl08LXN1bW1hcnkudGFibGVbLDFdCnN1bW1hcnkudGFibGUudG90YWw8LXRlbXAKCgpzdW1tYXJ5LnRhYmxlPC1jaGFyYWN0ZXIoKQoKZm9yKGkgaW4gMTpsZW5ndGgodmFyLm5hbWVzKSl7CiAgCiAgICB0ZW1wLm1hdHJpeDwtYXMubWF0cml4KHRhYmxlKGN0Lm1hdHJpeFtseW1waG9ubHkuaWQsdmFyLm5hbWVzW2ldXSkpCiAgICByb3duYW1lcyh0ZW1wLm1hdHJpeCk8LXBhc3RlKHZhci5uYW1lc1tpXSwiIC0gIixyb3duYW1lcyh0ZW1wLm1hdHJpeCkpCiAgICAKICAgIHRlbXAuc3VtPC1zdW0odGVtcC5tYXRyaXgpCiAgICB0ZW1wLm1hdHJpeDI8LXRlbXAubWF0cml4L3RlbXAuc3VtCiAgICBzdW1tYXJ5LnRhYmxlPC1yYmluZChzdW1tYXJ5LnRhYmxlLGFzLm1hdHJpeChwYXN0ZSh0ZW1wLm1hdHJpeCwiKCIscm91bmQodGVtcC5tYXRyaXgyLGRpZ2l0cz0yKSwiKSIsc2VwPSIiKSkpCiAgICByb3duYW1lcyhzdW1tYXJ5LnRhYmxlKVsobnJvdyhzdW1tYXJ5LnRhYmxlKS1ucm93KHRlbXAubWF0cml4KSsxKTpucm93KHN1bW1hcnkudGFibGUpXTwtcm93bmFtZXModGVtcC5tYXRyaXgpCn0KCmNvbG5hbWVzKHN1bW1hcnkudGFibGUpWzFdPC0iTHltcGggb25seSIKCgp0ZW1wLm5hbWVzPC11bmlvbihyb3duYW1lcyhzdW1tYXJ5LnRhYmxlLnRvdGFsKSxyb3duYW1lcyhzdW1tYXJ5LnRhYmxlKSkKdGVtcDwtbWF0cml4KCIiLG5yb3c9bGVuZ3RoKHRlbXAubmFtZXMpLG5jb2w9MykKcm93bmFtZXModGVtcCk8LXRlbXAubmFtZXMKdGVtcFtyb3duYW1lcyhzdW1tYXJ5LnRhYmxlLnRvdGFsKSwxXTwtc3VtbWFyeS50YWJsZS50b3RhbFssMV0KdGVtcFtyb3duYW1lcyhzdW1tYXJ5LnRhYmxlLnRvdGFsKSwyXTwtc3VtbWFyeS50YWJsZS50b3RhbFssMl0KdGVtcFtyb3duYW1lcyhzdW1tYXJ5LnRhYmxlKSwzXTwtc3VtbWFyeS50YWJsZVssMV0KCnN1bW1hcnkudGFibGUudG90YWw8LXRlbXAKCmNvbG5hbWVzKHN1bW1hcnkudGFibGUudG90YWwpPC1jKCJvdmVybGFwIiwiU3RhZ2UgMSBvbmx5IiwiTHltcGggb25seSIpCgoKIyBjYWxjdWxhdGUgdGhlIHAgdmFsdWVzIHRvIGFzc2VzcyB0aGUgZGlmZmVyZW5jZXMgYmV0d2VlbiB0aGUgdGhyZWUgZ3JvdXBzIG9mIHBhdGllbnRzIGZvciBlYWNoIHRyYWl0CgpwdmFsdWUudmVjdDwtbnVtZXJpYygpCgpmb3IoaSBpbiAxOmxlbmd0aCh2YXIubmFtZXMpKXsKICAKICAgIHRlbXAubWF0cml4PC1hcy5tYXRyaXgodGFibGUoY3QubWF0cml4W2Mob3ZlcmxhcC5pZCxzdGFnZTFvbmx5LmlkLGx5bXBob25seS5pZCksdmFyLm5hbWVzW2ldXSkpCiAgICByb3duYW1lcyh0ZW1wLm1hdHJpeCk8LXBhc3RlKHZhci5uYW1lc1tpXSwiIC0gIixyb3duYW1lcyh0ZW1wLm1hdHJpeCkpCiAgICAKICAgIHRlbXAxPC1jdC5tYXRyaXhbYyhvdmVybGFwLmlkLHN0YWdlMW9ubHkuaWQsbHltcGhvbmx5LmlkKSx2YXIubmFtZXNbaV1dCiAgICB0ZW1wMjwtYyhyZXAoIm92ZXJsYXAiLGxlbmd0aChvdmVybGFwLmlkKSkscmVwKCJzdGFnZTEiLGxlbmd0aChzdGFnZTFvbmx5LmlkKSkscmVwKCJseW1waCIsbGVuZ3RoKGx5bXBob25seS5pZCkpKQogICAgdGVtcC5pbmRleDwtMTpsZW5ndGgodGVtcDEpCiAgICB0ZW1wLmluZGV4PC10ZW1wLmluZGV4WyEoaXMubmEodGVtcDEpIHwgaXMubmEodGVtcDIpKV0KICAgIHRlbXAxPC10ZW1wMVt0ZW1wLmluZGV4XQogICAgdGVtcDI8LXRlbXAyW3RlbXAuaW5kZXhdCiAgICAKICAgIGlmKGxlbmd0aCh1bmlxdWUodGVtcDEpKT09MSl7CiAgICAgIHB2YWx1ZS52ZWN0PC1jKHB2YWx1ZS52ZWN0LCByZXAoMSxsZW5ndGgodW5pcXVlKHRlbXAxKSkpKQogICAgICBuYW1lcyhwdmFsdWUudmVjdClbbGVuZ3RoKHB2YWx1ZS52ZWN0KV08LXJvd25hbWVzKHRlbXAubWF0cml4KQogICAgfWVsc2V7CiAgICAgIHB2YWx1ZS52ZWN0PC1jKHB2YWx1ZS52ZWN0LCByZXAoY2hpc3EudGVzdCh0ZW1wMSxhcy5mYWN0b3IodGVtcDIpKSRwLnZhbHVlLGxlbmd0aCh1bmlxdWUodGVtcDEpKSkpICAKICAgICAgbmFtZXMocHZhbHVlLnZlY3QpWyhsZW5ndGgocHZhbHVlLnZlY3QpLWxlbmd0aCh1bmlxdWUodGVtcDEpKSsxKTpsZW5ndGgocHZhbHVlLnZlY3QpXTwtcm93bmFtZXModGVtcC5tYXRyaXgpCiAgICB9Cn0KCnN1bW1hcnkudGFibGUudG90YWw8LWNiaW5kKHN1bW1hcnkudGFibGUudG90YWwscHZhbHVlLnZlY3Rbcm93bmFtZXMoc3VtbWFyeS50YWJsZS50b3RhbCldKQpjb2xuYW1lcyhzdW1tYXJ5LnRhYmxlLnRvdGFsKVs0XTwtInAgdmFsdWUiCgpvdXRwdXQuZmlsZXBhdGg8LWZpbGUucGF0aChob21lLmRpciwic2NyYXRjaC9HUkFEUy9TQVJDX3Jlc3VsdHMvUmVzdWx0c19zdW1tYXJ5X1BCTUNfaGczOC9iYXNlbGluZS9HU0VBX2tub3duZ2VuZXNfREVTZXEyX2xvZzJhZGp1c3RlZC9ncmFkc2lkX292ZXJsYXBfbm9ub3ZlcmxhcF9jdGZlYXR1cmVzLnhsc3giKQp3cml0ZS54bHN4KHN1bW1hcnkudGFibGUudG90YWwsZmlsZT1vdXRwdXQuZmlsZXBhdGgscm93Lm5hbWVzPVQsY29sLm5hbWVzPVQsYXBwZW5kPUYpCmBgYAoKCldlIGFsc28gZ2VuZXJhdGVkIHRoZSBHU0VBIGlucHV0IGZpbGVzIGZvciB0aHJlZSB0aHJlZSBncm91cHMgb2YgcGF0aWVudHMgdXNpbmcgdGhlIGZ1bmN0aW9uIHdlIHdyb3RlLgpgYGB7cn0KCiMgb3V0cHV0IHRoZSBtZXJnZWQgY2xpbmljYWwgZGF0YSBtYXRyaXggYXMgYSB0eHQgZmlsZQpjbGluaWMuZmlsZXBhdGg8LSIvaG9tZS95YW54aXRpbmcvZHJpdmVyX0dyYWNlL3NjcmF0Y2gvR1JBRFMvU0FSQ19yZXN1bHRzL1Jlc3VsdHNfc3VtbWFyeV9QQk1DX2hnMzgvYmFzZWxpbmUvZGF0YS9jbGluaWNfbWF0cml4X21lcmdlZC5SRFMiCmNsaW5pYy5tYXRyaXg8LXJlYWRSRFMoY2xpbmljLmZpbGVwYXRoLHJlZmhvb2sgPSBOVUxMKQpvdXRwdXQuZmlsZXBhdGg8LSIvaG9tZS95YW54aXRpbmcvZHJpdmVyX0dyYWNlL3NjcmF0Y2gvR1JBRFMvU0FSQ19yZXN1bHRzL1Jlc3VsdHNfc3VtbWFyeV9QQk1DX2hnMzgvYmFzZWxpbmUvZGF0YS9jbGluaWNfbWF0cml4X21lcmdlZC50eHQiCndyaXRlLnRhYmxlKGNsaW5pYy5tYXRyaXgsZmlsZT1vdXRwdXQuZmlsZXBhdGgsc2VwPSJcdCIscm93Lm5hbWVzPUYsY29sLm5hbWVzPVQscXVvdGU9RixhcHBlbmQ9RikKCgojIG5vdGUgdGhpcyBmdW5jdGlvbiBpcyBzbGlnaHRseSBkaWZmZXJlbnQgZm9yIERFU2VxMiBOb3JtIGRhdGEgYW5kIHRoZSBUUE0gbWF0cml4Cm15X2dzZWFfZmlsZWNyZWF0ZV9iaW5hcnk8LWZ1bmN0aW9uKGZwa20uZmlsZXBhdGgsY2xpbmljLmZpbGVwYXRoLHZhci5uYW1lcyxncm91cDEudmFsdWVzLGdyb3VwMi52YWx1ZXMsZ3JvdXAxLm5hbWU9Imdyb3VwMSIsZ3JvdXAyLm5hbWU9Imdyb3VwMiIsb3V0cHV0LmRpcixzdWZmaXgubmFtZSl7CiAgIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKICAjIEFyZ3VtZW50czoKICAjIAogICMgZ2V4cC5maWxlcGF0aCBpcyB0aGUgZmlsZSBwYXRoIG9mIHRoZSBUUE0gbWF0cml4IHdpdGggZmlyc3QgNSBjb2x1bW5zIGJlaW5nIGFubm90YXRpb24gZm9yIGdlbmVzIAogICMgKFRQTV9iYXNlbGluZV8yNzZfY2xlYW5fY2VsbGRpZmZhZGp1c3RlZF93aXRoYW5ub3QudHh0KS4KICAjIAogICMgY2xpbmljLmZpbHBhdGggaXMgdGhlIGZpbGUgcGF0aCBvZiB0aGUgY2xpbmljIGRhdGEgKGNsaW5pY19tYXRyaXhfbWVyZ2VkLnR4dCkuIFRoZSBmaXJzdCByb3cgbmVlZHMgdG8gYmUgdGhlIGNvbHVtbiBuYW1lcy4KICAjIAogICMgdmFyLm5hbWVzIGlzIGEgdmVjdG9yIGNvbnRhaW5pbmcgdGhlIG5hbWVzIG9mIGNvbHVtbnMgaW4gY2xpbmljLmZpbGVwYXRoIHRoYXQgeW91IHdhbnQgdG8gZ2VuZXJhdGUgdGhlIHBoZW5vdHlwZSBmaWxlIGZvci4KICAjCiAgIyBncm91cDEudmFsdWVzIGlzIGEgZGF0YS5mcmFtZSBvZiBzdHJpbmcgZGVzY3JpYmluZyB2YWx1ZXMgb2YgdmFyLm5hbWVzIHRoYXQgYXJlIGNvbnNpZGVyZWQgYXMgZ29ydXAgMS4gCiAgIyBGb3IgZXhhbXBsZSwgZ3JvdXAxLnZhbHVlcz1kYXRhLmZyYW1lKFBIRU5HUlA9Im5vbmFjdXRlIFN0YWdlIEkgdW50cmVhdGVkIikuIE5vdGUgdGhhdCB0aGUgY29sdW1ucyBpbiBncm91cDEudmFsdWVzIG5lZWQgdG8gbWF0Y2ggdGhvc2UgaW4gdmFyLm5hbWVzIGlmIHRoZXJlIGFyZQogICMgbW9yZSB0aGFuIDEgdmFyaWFibGUgdG8gZGVmaW5lIHRoZSBncm91cHMuCiAgIwogICMgZ3JvdXAyLnZhbHVlcyBoYXMgdGhlIHNhbWUgZm9ybWF0IGFzIGdyb3VwMS52YWx1ZXMgYnV0IGlzIHRoZSBzZXR0aW5ncyBmb3IgZ3JvdXAyLgogICMKICAjIGdyb3VwMS5uYW1lIGlzIHRoZSBuYW1lIHlvdSB3YW50IHRvIGNhbGwgeW91ciBncm91cDEgaW4gdGhlIGNscyBmaWxlLgogICMKICAjIGdyb3VwMi5uYW1lIGlzIHRoZSBuYW1lIHlvdSB3YW50IHRvIGNhbGwgeW91ciBncm91cDIgaW4gdGhlIGNscyBmaWxlLgogICMgCiAgIyBvdXRwdXQuZGlyIGlzIHRoZSBmb2xkZXIgbmFtZSB3aGVyZSB5b3Ugd2FudCB0byBzYXZlIHRoZSBjcmVhdGVkIGZpbGVzLgogICMKICAjIHN1ZmZpeC5uYW1lIGlzIHRoZSBzdWZmaXggbmFtZSBpbiB0aGUgbmFtZSBvZiB0aGUgb3V0cHV0IGZpbGVzLiBUaGUgZ2N0IGZpbGUgd2lsbCBiZSBuYW1lZCBhcyBnY3Rfc3VmZml4Lm5hbWUuZ2N0IGFuZCBjbHMgZmlsZSB3aWxsIGJlIG5hbWVkIGFzCiAgIyBjbHNfc3VmZml4Lm5hbWUuZ2N0LiBGb3IgZXhhbXBsZSwgaWYgc3VmZml4Lm5hbWU9InRlc3QiLCB0aGUgZ2N0IGZpbGUgd2lsbCBiZSBuYW1lZCAiZ2N0X3Rlc3QuZ2N0IiBhbmQgdGhlIGNscyBmaWxlIHdpbGwgYmUgbmFtZWQgYXMgCiAgIyAiY2xzX3Rlc3QuY2xzIi4KICAjIAogICMgVmFsdWU6CiAgIwogICMgVGhpcyBmdW5jdGlvbiB3aWxsIHJldHVybiAwIGlmIHN1Y2Nlc3NmdWxseSByYW4uIE90aGVyd2lzZSwgaXQgd2lsbCByZXR1cm4gMS4gSWYgdW5zdWNjZXNzZnVsLCBpbmZvcm1hdGlvbiByZWdhcmRpbmcgd2hhdCB3ZW50IHdyb25nIHdpbGwgYmUgc3BpdCAKICAjIG91dCBhcyB3YXJuaW5nIG1lc3NhZ2VzLgogICMKICAjIFdoZW4gcmFuIHN1Y2Nlc3NmdWxseSwgdGhpcyBmdW5jdGlvbiB3aWxsIGNyZWF0ZSB0d28gZmlsZXMgdW5kZXIgdGhlIHNwZWNpZmllZCBvdXRwdXQuZGlyIG5hbWVkIGdjdF92YXIubmFtZS5nY3QgYW5kIGNsc192YXIubmFtZS5jbHMsIHdoZXJlIAogICMgdmFyLm5hbWUgd2lsbCBiZSB0aGUgc3BlZmljaWVkIHZhbHVlIAogICMgaW4gdGhlIGFyZ3VtZW50LiBUaGVzZSB0d28gZmlsZXMgY2FuIGJlIGRpcmVjdGx5IGxvYWRlZCBpbnRvIEdTRUEgdG9nZXRoZXIgd2l0aCBhbm90aGVyIGdlbmUgc2V0IGZpbGUgdGhhdCBuZWVkcyB0byBiZSBnZW5lcmF0ZWQgb3V0c2lkZSBvZiB0aGlzIAogICMgZnVuY3Rpb24uCiAgIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKICAKICAjICBnZW5lcmF0ZSB0aGUgZ2VuZSBleHByZXNzaW9uIG1hdHJpeCBpbnB1dCBmaWxlIGZvciBHU0VBCiAgI2Zwa20uZmlsZXBhdGg8LWZpbGUucGF0aChob21lLmRpciwic2NyYXRjaC9HUkFEUy9TQVJDX3Jlc3VsdHMvUmVzdWx0c19zdW1tYXJ5X1BCTUNfaGczOC9iYXNlbGluZS9kYXRhX2FkanVzdGVkMiIsIlRQTV9iYXNlbGluZV8yNzZfY2xlYW5fY2VsbGRpZmZhZGp1c3RlZF93aXRoYW5ub3QudHh0IikKICAjY2xpbmljLmZpbGVwYXRoPC0iL2hvbWUveWFueGl0aW5nL2RyaXZlcl9HcmFjZS9zY3JhdGNoL0dSQURTL1NBUkNfcmVzdWx0cy9SZXN1bHRzX3N1bW1hcnlfUEJNQ19oZzM4L2Jhc2VsaW5lL2RhdGEvY2xpbmljX21hdHJpeF9tZXJnZWQudHh0IgogICNvdXRwdXQuZGlyPC0iL2hvbWUveWFueGl0aW5nL2RyaXZlcl9HcmFjZS9zY3JhdGNoL0dSQURTL1NBUkNfcmVzdWx0cy9SZXN1bHRzX3N1bW1hcnlfUEJNQ19oZzM4L2Jhc2VsaW5lL0dTRUFfa25vd25nZW5lcyIKICAjIHN1ZmZpeC5uYW1lPC0iYW5hbHlzaXMxIgogIAogIAogICMgbG9hZCBpbiB0aGUgZnBrbSBtYXRyaXgKICBmcGttLm1hdHJpeDwtcmVhZC50YWJsZShmcGttLmZpbGVwYXRoLHNlcD0iXHQiLGhlYWRlcj1ULGNoZWNrLm5hbWVzPUYsYXMuaXM9VFJVRSxjb21tZW50LmNoYXIgPSAiIikKICBmcGttLm1hdHJpeC5hbm5vPC1mcGttLm1hdHJpeFssMTo2XQogIGZwa20ubWF0cml4PC1mcGttLm1hdHJpeFssNzpuY29sKGZwa20ubWF0cml4KV0KICAKICAjIGxvYWQgaW4gdGhlIGNsaW5pY2FsIGRhdGEgYW5kIHN1YnNldCB0aGUgc2FtcGxlcyB0byB0aG9zZSBsaXN0ZWQgaW4gdGhlIGZwa20ubWF0cml4CiAgY2xpbmljLm1hdHJpeD1yZWFkLnRhYmxlKGNsaW5pYy5maWxlcGF0aCxzZXA9Ilx0IixoZWFkZXI9VCxjaGVjay5uYW1lcz1GLHN0cmluZ3NBc0ZhY3RvcnM9RikKICByb3duYW1lcyhjbGluaWMubWF0cml4KTwtYXMubWF0cml4KGNsaW5pYy5tYXRyaXgpWywxXSAjIEdSQURTIElECiAgY2xpbmljLm1hdHJpeDwtY2xpbmljLm1hdHJpeFtjb2xuYW1lcyhmcGttLm1hdHJpeCksXQoKICAjIGJhc2VkIG9uIHZhci5uYW1lLCBncm91cDEudmFsdWVzLCBhbmQgZ3JvdXAyLnZhbHVlcywgaWRlbnRpZnkgdGhlIHNhbXBsZXMgdG8gaW5jbHVkZSBpbiB0aGUgZmlsZXMuCiAgaWYoZmlsZS5leGlzdHMob3V0cHV0LmRpcik9PUYpewogICAgZGlyLmNyZWF0ZShvdXRwdXQuZGlyKQogIH0KICAKICBnY3QuZmlsZXBhdGg8LWZpbGUucGF0aChvdXRwdXQuZGlyLHBhc3RlKCJnZXhwXyIsc3VmZml4Lm5hbWUsIi5nY3QiLHNlcD0iIikpCiAgY2xzLmZpbGVwYXRoPC1maWxlLnBhdGgob3V0cHV0LmRpcixwYXN0ZSgiY2xzXyIsc3VmZml4Lm5hbWUsIi5jbHMiLHNlcD0iIikpCiAgCiAgIyBzdWJzdHJhY3Qgc2FtcGxlcyB3aXRoIHZhci5uYW1lIGVxdWFsIHRvIHRoZSB2YWx1ZXMgaW4gZ3JvdXAxLnZhbHVlcyBhbmQgZ3JvdXAyLiB2YWx1ZXMKICB0ZW1wMTwtZnBrbS5tYXRyaXhbLGFwcGx5KGFzLmRhdGEuZnJhbWUoY2xpbmljLm1hdHJpeFssdmFyLm5hbWVzXSksMSxwYXN0ZSxjb2xsYXBzZT0iXyIpJWluJWFwcGx5KGdyb3VwMS52YWx1ZXMsMSxwYXN0ZSxjb2xsYXBzZT0iXyIpXQogIHRlbXAyPC1mcGttLm1hdHJpeFssYXBwbHkoYXMuZGF0YS5mcmFtZShjbGluaWMubWF0cml4Wyx2YXIubmFtZXNdKSwxLHBhc3RlLGNvbGxhcHNlPSJfIiklaW4lYXBwbHkoZ3JvdXAyLnZhbHVlcywxLHBhc3RlLGNvbGxhcHNlPSJfIildCiAgY2F0KCJUaGVyZSBhcmUgIixuY29sKHRlbXAxKSwiIGFuZCAiLCBuY29sKHRlbXAyKSwiIHNhbXBsZXMgZm9yIGdyb3VwMSBhbmQgZ3JvdXAyLCByZXNwZWN0aXZlbHkuXG4iKQogIGRhdGEubWF0cml4PC1jYmluZCh0ZW1wMSx0ZW1wMikKICBkYXRhLm1hdHJpeDwtY2JpbmQoZnBrbS5tYXRyaXguYW5ub1ssMV0scmVwKCJuYSIsbnJvdyhkYXRhLm1hdHJpeCkpLGRhdGEubWF0cml4KQogIGNvbG5hbWVzKGRhdGEubWF0cml4KVsxOjJdPC1jKCJOQU1FIiwiRGVzY3JpcHRpb24iKQogIHBoZW5vLnZlY3Q8LWMocmVwKDAsbmNvbCh0ZW1wMSkpLHJlcCgxLG5jb2wodGVtcDIpKSkKICAKICAjIG91dHB1dCB0aGUgZ2VuZSBleHByZXNzaW9uIGRhdGEKICBjbWQub3V0PC0iIzEuMlxuIgogIGNtZC5vdXQ8LXBhc3RlKGNtZC5vdXQsbnJvdyhkYXRhLm1hdHJpeCksIlx0IixuY29sKGRhdGEubWF0cml4KS0yLCJcbiIsc2VwPSIiKQogIGNhdChjbWQub3V0LGZpbGU9Z2N0LmZpbGVwYXRoLGFwcGVuZD1GKQogIHdyaXRlLnRhYmxlKGRhdGEubWF0cml4LGZpbGU9Z2N0LmZpbGVwYXRoLGFwcGVuZD1ULHJvdy5uYW1lcz1GLGNvbC5uYW1lcz1ULHF1b3RlPUYpCiAgCiAgIyBvdXRwdXQgdGhlIHBoZW5vdHlwZSBmaWxlCiAgY21kLm91dDwtcGFzdGUobmNvbChkYXRhLm1hdHJpeCktMiwiXHQiLDIsIlx0IiwxLCJcbiIsc2VwPSIiKQogIGNtZC5vdXQ8LXBhc3RlKGNtZC5vdXQsIiMgZ3JvdXAxIGdyb3VwMlxuIixzZXA9IiIpCiAgY2F0KGNtZC5vdXQsZmlsZT1jbHMuZmlsZXBhdGgsYXBwZW5kPUYsc2VwPSIiKQogIGNhdChwaGVuby52ZWN0LGZpbGU9Y2xzLmZpbGVwYXRoLGFwcGVuZD1ULHNlcD0iICIpCiAgCiAgcmV0dXJuKDApCn0KCgoKZnBrbS5maWxlPWZpbGUucGF0aChob21lLmRpciwic2NyYXRjaC9HUkFEUy9TQVJDX3Jlc3VsdHMvUmVzdWx0c19zdW1tYXJ5X1BCTUNfaGczOC9iYXNlbGluZS9kYXRhX2FkanVzdGVkMiIsIkRFU2VxMl9ub3JtYWxpemVkXzI3Nl9jbGVhbl9sb2cyX2NlbGxkaWZmYWRqdXN0ZWRfd2l0aGFubm90LnR4dCIpCmNsaW5pYy5maWxlPC0iL2hvbWUveWFueGl0aW5nL2RyaXZlcl9HcmFjZS9zY3JhdGNoL0dSQURTL1NBUkNfcmVzdWx0cy9SZXN1bHRzX3N1bW1hcnlfUEJNQ19oZzM4L2Jhc2VsaW5lL2RhdGEvY2xpbmljX21hdHJpeF9tZXJnZWQudHh0IgpvdXRwdXQuZm9sZGVyPC0iL2hvbWUveWFueGl0aW5nL2RyaXZlcl9HcmFjZS9zY3JhdGNoL0dSQURTL1NBUkNfcmVzdWx0cy9SZXN1bHRzX3N1bW1hcnlfUEJNQ19oZzM4L2Jhc2VsaW5lL0dTRUFfa25vd25nZW5lc19ERVNlcTJfbG9nMmFkanVzdGVkIgoKIyBnZW5lcmF0ZSB0aGUgZmlsZXMgZm9yIGFuYWx5c2lzIDk6IG92ZXJsYXBwZWQgcGF0aWVudHMKdmFybmFtZXM8LWMoIlBIRU5HUlAiLCJub2R1bGVfbHltcGhfcGhlbm8iKQpteV9nc2VhX2ZpbGVjcmVhdGVfYmluYXJ5KGZwa20uZmlsZXBhdGg9ZnBrbS5maWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgIGNsaW5pYy5maWxlcGF0aCA9IGNsaW5pYy5maWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgIHZhci5uYW1lcyA9IHZhcm5hbWVzLAogICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwMS52YWx1ZXMgPSBkYXRhLmZyYW1lKFBIRU5HUlA9YygiTm9uLWFjdXRlLCBTdGFnZSBJLCB1bnRyZWF0ZWQiKSxub2R1bGVfbHltcGhfcGhlbm89YygibHltcGgiKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAyLnZhbHVlcyA9IGRhdGEuZnJhbWUoUEhFTkdSUD1jKCJTdGFnZSBJSS1JSUksIHVudHJlYXRlZCIsIlN0YWdlIElJLUlJSSwgdW50cmVhdGVkIiwiU3RhZ2UgSUktSUlJLCB1bnRyZWF0ZWQiLCJTdGFnZSBJViwgdW50cmVhdGVkIiwiU3RhZ2UgSVYsIHVudHJlYXRlZCIsIlN0YWdlIElWLCB1bnRyZWF0ZWQiKSxub2R1bGVfbHltcGhfcGhlbm89YygibHltcGgiLCJtaWNyb25vZHVsZSIsImJvdGgiLCJseW1waCIsIm1pY3Jvbm9kdWxlIiwiYm90aCIpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBvdXRwdXQuZGlyPW91dHB1dC5mb2xkZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgc3VmZml4Lm5hbWU9ImFuYWx5c2lzOSIKICAgICAgICAgICAgICAgICAgICAgICAgICApCgojIGdlbmVyYXRlIHRoZSBmaWxlcyBmb3IgYW5hbHlzaXMgMTA6IFN0YWdlIEkgb25seQp2YXJuYW1lczwtYygiUEhFTkdSUCIsIm5vZHVsZV9seW1waF9waGVubyIpCm15X2dzZWFfZmlsZWNyZWF0ZV9iaW5hcnkoZnBrbS5maWxlcGF0aD1mcGttLmZpbGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgY2xpbmljLmZpbGVwYXRoID0gY2xpbmljLmZpbGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyLm5hbWVzID0gdmFybmFtZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAxLnZhbHVlcyA9IGRhdGEuZnJhbWUoUEhFTkdSUD1jKCJOb24tYWN1dGUsIFN0YWdlIEksIHVudHJlYXRlZCIsIk5vbi1hY3V0ZSwgU3RhZ2UgSSwgdW50cmVhdGVkIiksbm9kdWxlX2x5bXBoX3BoZW5vPWMoIm1pY3Jvbm9kdWxlIiwiYm90aCIpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cDIudmFsdWVzID0gZGF0YS5mcmFtZShQSEVOR1JQPWMoIlN0YWdlIElJLUlJSSwgdW50cmVhdGVkIiwiU3RhZ2UgSUktSUlJLCB1bnRyZWF0ZWQiLCJTdGFnZSBJSS1JSUksIHVudHJlYXRlZCIsIlN0YWdlIElWLCB1bnRyZWF0ZWQiLCJTdGFnZSBJViwgdW50cmVhdGVkIiwiU3RhZ2UgSVYsIHVudHJlYXRlZCIpLG5vZHVsZV9seW1waF9waGVubz1jKCJseW1waCIsIm1pY3Jvbm9kdWxlIiwiYm90aCIsImx5bXBoIiwibWljcm9ub2R1bGUiLCJib3RoIikpLAogICAgICAgICAgICAgICAgICAgICAgICAgIG91dHB1dC5kaXI9b3V0cHV0LmZvbGRlciwKICAgICAgICAgICAgICAgICAgICAgICAgICBzdWZmaXgubmFtZT0iYW5hbHlzaXMxMCIKICAgICAgICAgICAgICAgICAgICAgICAgICApCgojIGdlbmVyYXRlIHRoZSBmaWxlcyBmb3IgYW5hbHlzaXMgMTE6IGx5bXBoIG9ubHkgKG5vIHN0YWdlIEkpCnZhcm5hbWVzPC1jKCJQSEVOR1JQIiwibm9kdWxlX2x5bXBoX3BoZW5vIikKcGhlbi52YWx1ZTwtYygiTm9uLWFjdXRlLCBTdGFnZSBJLCB1bnRyZWF0ZWQiLCJDYXJkaWFjIGRlZmluaW5nIHRoZXJhcHkiLCJTdGFnZSBJViwgdHJlYXRlZCIsIlN0YWdlIElWLCB1bnRyZWF0ZWQiLCJTdGFnZSBJSS1JSUksIHVudHJlYXRlZCIsIlJlbWl0dGluZywgdW50cmVhdGVkIiwiTXVsdGktb3JnYW4iLCAiU3RhZ2UgSUktSUlJLCB0cmVhdGVkIiwiQWN1dGUgU2FyY29pZG9zaXMsIHVudHJlYXRlZCIpCnBoZW4udmFsdWU8LXNldGRpZmYocGhlbi52YWx1ZSwiTm9uLWFjdXRlLCBTdGFnZSBJLCB1bnRyZWF0ZWQiKQoKbXlfZ3NlYV9maWxlY3JlYXRlX2JpbmFyeShmcGttLmZpbGVwYXRoPWZwa20uZmlsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICBjbGluaWMuZmlsZXBhdGggPSBjbGluaWMuZmlsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICB2YXIubmFtZXMgPSB2YXJuYW1lcywKICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cDEudmFsdWVzID0gZGF0YS5mcmFtZShQSEVOR1JQPXBoZW4udmFsdWUsbm9kdWxlX2x5bXBoX3BoZW5vPXJlcCgibHltcGgiLGxlbmd0aChwaGVuLnZhbHVlKSkpLAogICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwMi52YWx1ZXMgPSBkYXRhLmZyYW1lKFBIRU5HUlA9YygiU3RhZ2UgSUktSUlJLCB1bnRyZWF0ZWQiLCJTdGFnZSBJSS1JSUksIHVudHJlYXRlZCIsIlN0YWdlIElJLUlJSSwgdW50cmVhdGVkIiwiU3RhZ2UgSVYsIHVudHJlYXRlZCIsIlN0YWdlIElWLCB1bnRyZWF0ZWQiLCJTdGFnZSBJViwgdW50cmVhdGVkIiksbm9kdWxlX2x5bXBoX3BoZW5vPWMoImx5bXBoIiwibWljcm9ub2R1bGUiLCJib3RoIiwibHltcGgiLCJtaWNyb25vZHVsZSIsImJvdGgiKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgb3V0cHV0LmRpcj1vdXRwdXQuZm9sZGVyLAogICAgICAgICAgICAgICAgICAgICAgICAgIHN1ZmZpeC5uYW1lPSJhbmFseXNpczExIgogICAgICAgICAgICAgICAgICAgICAgICAgICkKCgpgYGAKCiMjIENvbXBhcmluZyB0byAxMFggR2Vub21pY3ggZGF0YQoKV2UgZG93bmxvYWRlZCBhIHByZXZpb3VzbHkgcHVibGlzaGVkIDEwWCBHZW5vbWljeCBkYXRhIGFuZCBjb21wYXJlZCB0aGUgZ2VuZXMgaW4gQmxvb20gZGF0YSB0byBzZWUgaWYgdGhleSBhcmUgZW5yaWNoZWQgaW4gY2VsbCB0eXBlIG1hcmtlcnMgb3Igd2hpY2ggY2VsbCB0eXBlcyBhcmUgZXhwcmVzc2luZyB0aGVtLgoKCiMjIyBTdGFuZGFyZCBTZXVyYXQKCkZpcnN0LCB3ZSBsb2FkIGluIHRoZSByYXcgY291bnQgZGF0YSBhbmQgcmVkbyB0aGUgcHJlcHJvY2Vzc2luZyB1c2luZyBzdGFuZGFyZCBTZXVyYXQgcGlwZWxpbmUuCmBgYHtyIGZpZy53aWR0aD04LGZpZy53aWR0aD04fQojIGxvYWQgaW4gdGhlIHNjUk5BLXNlcSBkYXRhIG9mIFNBUkMgUEJNQyBzYW1wbGVzIHdpdGggYW5ub3RhdGlvbnMgdGhhdCB3YXMgcHJldmlvdXNseSBwdWJsaXNoZWQKbGlicmFyeShNYXRyaXgpCmNvdW50LmZpbGVwYXRoPC0iL2hvbWUveWFueGl0aW5nL0RvY3VtZW50cy9SZXNlYXJjaC9HUkFEU19TQVJDX1BCTUMvRGF0YS9HU0UxMzIzMzhfbWF0cml4X2NvdW50cy5tdHgiCmNvbGRhdGEuZmlsZXBhdGg8LSIvaG9tZS95YW54aXRpbmcvRG9jdW1lbnRzL1Jlc2VhcmNoL0dSQURTX1NBUkNfUEJNQy9EYXRhL0dTRTEzMjMzOF9tYXRyaXhfY29sRGF0YS50eHQiCnJvd2RhdGEuZmlsZXBhdGg8LSIvaG9tZS95YW54aXRpbmcvRG9jdW1lbnRzL1Jlc2VhcmNoL0dSQURTX1NBUkNfUEJNQy9EYXRhL0dTRTEzMjMzOF9tYXRyaXhfcm93RGF0YS5tdHgiCmRhdGEuZmlsZXBhdGg8LSIvaG9tZS95YW54aXRpbmcvRG9jdW1lbnRzL1Jlc2VhcmNoL0dSQURTX1NBUkNfUEJNQy9EYXRhL0dTRTEzMjMzOF9TaW5nbGVDZWxsRXhwZXJpbWVudC5SRFMiCnNjLmRhdGE8LXJlYWRSRFMoZGF0YS5maWxlcGF0aCxyZWZob29rPU5VTEwpCgpjb3VudC5kYXRhPC1yZWFkTU0oY291bnQuZmlsZXBhdGgpCm15Y29sLmRhdGE8LXJlYWQudGFibGUoY29sZGF0YS5maWxlcGF0aCxzZXA9Ilx0Iixyb3cubmFtZXM9MSxoZWFkZXI9VCkKbXlyb3cuZGF0YTwtcmVhZExpbmVzKHJvd2RhdGEuZmlsZXBhdGgpCgojIGNyZWF0ZSBhIHNldXJhdCBvYmplY3Qgd2l0aCB0aGUgY291bnQgZGF0YSBhbmQgcHV0IHRoZSBpbnRlZ3JhdGVkIGRhdGEgaW4Kcm93bmFtZXMoY291bnQuZGF0YSk8LW15cm93LmRhdGFbMjpsZW5ndGgobXlyb3cuZGF0YSldCmNvbG5hbWVzKGNvdW50LmRhdGEpPC1yb3duYW1lcyhteWNvbC5kYXRhKQoKIyBjcmVhdGUgYSBTZXVyYXQgb2JqZWN0IHVzaW5nIHRoZSBjb3VudCBtYXRyaXgKbXkuZGF0YTwtQ3JlYXRlU2V1cmF0T2JqZWN0KGNvdW50cz1jb3VudC5kYXRhLHByb2plY3Q9IlBCTUMiLG1pbi5jZWxscz0wLG1pbi5mZWF0dXJlcyA9IDApCm15LmRhdGFbWyJwZXJjZW50Lm10Il1dIDwtIFBlcmNlbnRhZ2VGZWF0dXJlU2V0KG15LmRhdGEsIHBhdHRlcm4gPSAiXk1ULSIpCgojIFZpc3VhbGl6ZSBRQyBtZXRyaWNzIGFzIGEgdmlvbGluIHBsb3QKVmxuUGxvdChteS5kYXRhLCBmZWF0dXJlcyA9IGMoIm5GZWF0dXJlX1JOQSIsICJuQ291bnRfUk5BIiwgInBlcmNlbnQubXQiKSwgbmNvbCA9IDMpCgpwbG90MSA8LSBGZWF0dXJlU2NhdHRlcihteS5kYXRhLCBmZWF0dXJlMSA9ICJuQ291bnRfUk5BIiwgZmVhdHVyZTIgPSAicGVyY2VudC5tdCIpCnBsb3QyIDwtIEZlYXR1cmVTY2F0dGVyKG15LmRhdGEsIGZlYXR1cmUxID0gIm5Db3VudF9STkEiLCBmZWF0dXJlMiA9ICJuRmVhdHVyZV9STkEiKQpncmlkLmFycmFuZ2UocGxvdDEgLCBwbG90MixuY29sPTIpCgpteS5kYXRhIDwtIE5vcm1hbGl6ZURhdGEobXkuZGF0YSwgbm9ybWFsaXphdGlvbi5tZXRob2QgPSAiTG9nTm9ybWFsaXplIiwgc2NhbGUuZmFjdG9yID0gMTAwMDApCm15LmRhdGEgPC0gRmluZFZhcmlhYmxlRmVhdHVyZXMobXkuZGF0YSwgc2VsZWN0aW9uLm1ldGhvZCA9ICJ2c3QiLCBuZmVhdHVyZXMgPSAyMDAwKQoKIyBJZGVudGlmeSB0aGUgMTAgbW9zdCBoaWdobHkgdmFyaWFibGUgZ2VuZXMKdG9wMTAgPC0gaGVhZChWYXJpYWJsZUZlYXR1cmVzKG15LmRhdGEpLCAxMCkKCiMgcGxvdCB2YXJpYWJsZSBmZWF0dXJlcyB3aXRoIGFuZCB3aXRob3V0IGxhYmVscwpwbG90MSA8LSBWYXJpYWJsZUZlYXR1cmVQbG90KG15LmRhdGEpCnBsb3QyIDwtIExhYmVsUG9pbnRzKHBsb3QgPSBwbG90MSwgcG9pbnRzID0gdG9wMTAsIHJlcGVsID0gVFJVRSkKZ3JpZC5hcnJhbmdlKHBsb3QxLHBsb3QyLG5jb2w9MikKYGBgCgpgYGB7ciBmaWcuaGVpZ2h0PTgsZmlnLndpZHRoPTgsbWVzc2FnZT1GQUxTRX0KIyBzY2FsZSB0aGUgZGF0YQphbGwuZ2VuZXMgPC0gcm93bmFtZXMobXkuZGF0YSkKbXkuZGF0YSA8LSBTY2FsZURhdGEobXkuZGF0YSwgZmVhdHVyZXMgPSBhbGwuZ2VuZXMpCgojIGNvbmR1Y3QgUENBCm15LmRhdGEgPC0gUnVuUENBKG15LmRhdGEsIGZlYXR1cmVzID0gVmFyaWFibGVGZWF0dXJlcyhvYmplY3QgPSBteS5kYXRhKSkKVml6RGltTG9hZGluZ3MobXkuZGF0YSwgZGltcyA9IDE6MiwgcmVkdWN0aW9uID0gInBjYSIpCkRpbVBsb3QobXkuZGF0YSwgcmVkdWN0aW9uID0gInBjYSIpCmBgYAoKCmBgYHtyIGZpZy53aWR0aD0xMCxmaWcuaGVpZ2h0PTI0fQpEaW1IZWF0bWFwKG15LmRhdGEsIGRpbXMgPSAxOjE1LCBjZWxscyA9IDUwMCwgYmFsYW5jZWQgPSBUUlVFKQpgYGAKCmBgYHtyIGZpZy53aWR0aD04LGZpZy5oZWlnaHQ9OCxtZXNzYWdlPUZBTFNFfQojIGRlY2lkZSB0aGUgbnVtYmVyIG9mIFBDIHRvIHVzZQpteS5kYXRhIDwtIEphY2tTdHJhdyhteS5kYXRhLCBkaW1zID0gNDAsbnVtLnJlcGxpY2F0ZSA9IDEwMCkKbXkuZGF0YSA8LSBTY29yZUphY2tTdHJhdyhteS5kYXRhLCBkaW1zID0gMTo0MCkKSmFja1N0cmF3UGxvdChteS5kYXRhLCBkaW1zID0gMTo0MCkKYGBgCgpXZSBkZWNpZGVzIHRvIHVzZSB0aGUgdG9wIDIwIFBDcy4KYGBge3IgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9OH0KbnVtLnBjPC0yMApteS5kYXRhIDwtIEZpbmROZWlnaGJvcnMobXkuZGF0YSwgZGltcyA9IDE6bnVtLnBjKQpteS5kYXRhIDwtIEZpbmRDbHVzdGVycyhteS5kYXRhLCByZXNvbHV0aW9uID0gMC41KQpteS5kYXRhIDwtIFJ1blVNQVAobXkuZGF0YSwgZGltcyA9IDE6bnVtLnBjKQpEaW1QbG90KG15LmRhdGEsIHJlZHVjdGlvbiA9ICJ1bWFwIikKYGBgCgpEcmF3IHRoZSBVTUFQIHdpdGggYW5ub3RhdGlvbiBmcm9tIEdFTy4KCmBgYHtyIGZpZy53aWR0aD04LGZpZy5oZWlnaHQ9OCxtZXNzYWdlPUZBTFNFfQojIGFkZCB0aGUgY2VsbCB0eXBlIGFubm90YXRpb24gaW50byB0aGUgZGF0YQp0ZW1wLmRhdGE8LXNjLmRhdGFAbWV0YS5kYXRhCnRlbXAuZGF0YTwtdGVtcC5kYXRhW3Jvd25hbWVzKG15LmRhdGFAbWV0YS5kYXRhKSxdCm15LmRhdGFAbWV0YS5kYXRhPC1jYmluZChteS5kYXRhQG1ldGEuZGF0YSx0ZW1wLmRhdGEpCklkZW50cyhteS5kYXRhKTwtbXkuZGF0YUBtZXRhLmRhdGEkY2VsbHR5cGUKCm15LmRpc3RpbmN0LmNvbG9yczwtYygnI2U2MTk0YicgLCAnIzNjYjQ0YicsICcjZmZlMTE5JywgJyM0MzYzZDgnLCAnI2Y1ODIzMScsICcjOTExZWI0JywgJyM0NmYwZjAnLCAnI2YwMzJlNicsICcjYmNmNjBjJywgJyNmYWJlYmUnLCAnIzAwODA4MCcsICcjZTZiZWZmJywgJyM5YTYzMjQnLCAnIzgwODA4MCcsICcjODAwMDAwJywgICcjODA4MDAwJywgJyNmZmQ4YjEnLCAnIzAwMDA3NScsICAnI2ZmZmZmZicsICcjMDAwMDAwJywnI2ZmZmFjOCcsJyNhYWZmYzMnKQpjZWxsdHlwZS5jb2xvcnM8LW15LmRpc3RpbmN0LmNvbG9yc1sxOmxlbmd0aCh1bmlxdWUobXkuZGF0YUBtZXRhLmRhdGEkY2VsbHR5cGUpKV0KCkRpbVBsb3QobXkuZGF0YSwgcmVkdWN0aW9uID0gInVtYXAiLGNvbHM9Y2VsbHR5cGUuY29sb3JzLHNodWZmbGU9VCkKCklkZW50cyhteS5kYXRhKTwtbXkuZGF0YUBtZXRhLmRhdGEkZG9ub3IKRGltUGxvdChteS5kYXRhLCByZWR1Y3Rpb24gPSAidW1hcCIsc2h1ZmZsZT1UKQpgYGAKU2F2ZSB0aGUgU2V1cmF0IG9iamVjdC4KYGBge3J9CnNhdmVSRFMobXkuZGF0YSwgZmlsZSA9Ii9ob21lL3lhbnhpdGluZy9Eb2N1bWVudHMvUmVzZWFyY2gvR1JBRFNfU0FSQ19QQk1DL3NjUk5BX2NvbXBhcmUvR1NFMTMyMzM4X3NldXJhdF9ub2ludGVncmF0aW9uLnJkcyIpCmBgYAoKCldlIGNoZWNrIHRoZSBleHByZXNzaW9uIG9mIHRoZSBhbmFseXNpczRiIGdlbmVzIGluIGFsbCBjZWxsIHR5cGVzLgpgYGB7ciBmaWcud2lkdGg9MTAsZmlnLmhlaWdodD01LG1lc3NhZ2U9RkFMU0V9Cm15LmRpc3RpbmN0LmNvbG9yczwtYygnI2U2MTk0YicgLCAnIzNjYjQ0YicsICcjZmZlMTE5JywgJyM0MzYzZDgnLCAnI2Y1ODIzMScsICcjOTExZWI0JywgJyM0NmYwZjAnLCAnI2YwMzJlNicsICcjYmNmNjBjJywgJyNmYWJlYmUnLCAnIzAwODA4MCcsICcjZTZiZWZmJywgJyM5YTYzMjQnLCAnIzgwODA4MCcsICcjODAwMDAwJywgICcjODA4MDAwJywgJyNmZmQ4YjEnLCAnIzAwMDA3NScsICAnI2ZmZmZmZicsICcjMDAwMDAwJywnI2ZmZmFjOCcsJyNhYWZmYzMnKQpjZWxsdHlwZS5jb2xvcnM8LW15LmRpc3RpbmN0LmNvbG9yc1sxOmxlbmd0aCh1bmlxdWUobXkuZGF0YUBtZXRhLmRhdGEkY2VsbHR5cGUpKV0KYmxvb20uZmlsZXBhdGg8LSIvaG9tZS95YW54aXRpbmcvRG9jdW1lbnRzL1Jlc2VhcmNoL0dSQURTX1NBUkNfUEJNQy9EYXRhL2FuYWx5c2lzNGJfQkxPT00udHN2IgpibG9vbS5kYXRhPC1hcy5kYXRhLmZyYW1lKHJlYWRfdHN2KGJsb29tLmZpbGVwYXRoKSkKYmxvb20uZ2VuZXM8LWJsb29tLmRhdGFbYmxvb20uZGF0YSRgQ09SRSBFTlJJQ0hNRU5UYD09IlllcyIsIlNZTUJPTCJdCgp0ZW1wLm5hbWVzPC1pbnRlcnNlY3QoYmxvb20uZ2VuZXMscm93bmFtZXMoc2MuZGF0YUBhc3NheXMkaW50ZWdyYXRlZEBjb3VudHMpKQoKI291dHB1dC5maWxlcGF0aDwtIn4vZHJpdmVyX0dyYWNlL3NjcmF0Y2gvR1JBRFMvU0FSQ19yZXN1bHRzL1Jlc3VsdHNfc3VtbWFyeV9QQk1DX2hnMzgvYmFzZWxpbmUvc2NSTkFfY29tcGFyZS9mZWF0dXJlcGxvdF9ibG9vbV80Yi5wZGYiCiNvdXRwdXQuZmlsZXBhdGg8LSIvaG9tZS95YW54aXRpbmcvRG9jdW1lbnRzL1Jlc2VhcmNoL0dSQURTX1NBUkNfUEJNQy9zY1JOQV9jb21wYXJlL2ZlYXR1cmVwbG90X2Jsb29tXzRiLnBkZiIKI3BkZihvdXRwdXQuZmlsZXBhdGgsd2lkdGggPSA4LGhlaWdodD04LG9uZWZpbGUgPSBUKQojZy5saXN0PC1saXN0KCkKCmZvcihpIGluIDE6bGVuZ3RoKHRlbXAubmFtZXMpKXsKI3RlbXAuZGF0YTwtbXkuZGF0YUBtZXRhLmRhdGEKI2cuMTwtZ2dwbG90KHRlbXAuZGF0YSxhZXMoeD1VTUFQXzEseT1VTUFQXzIsY29sb3VyPWNlbGx0eXBlKSkrZ2VvbV9wb2ludChzaXplPTAuMSxzaGFwZT0xOSkrc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jZWxsdHlwZS5jb2xvcnMpK2d1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNpemUgPSA4KSkpCgpJZGVudHMobXkuZGF0YSk8LW15LmRhdGFAbWV0YS5kYXRhJGNlbGx0eXBlCmcuMTwtRGltUGxvdChteS5kYXRhLCByZWR1Y3Rpb24gPSAidW1hcCIsY29scz1jZWxsdHlwZS5jb2xvcnMsc2h1ZmZsZT1UKQpnLjI8LUZlYXR1cmVQbG90KG15LmRhdGEsZmVhdHVyZXM9dGVtcC5uYW1lc1tpXSxwdC5zaXplID0gMC4xLG9yZGVyPVQpCmc8LWdyaWQuYXJyYW5nZShnLjEsZy4yLG5jb2w9MikKCm91dHB1dC5maWxlcGF0aDwtZmlsZS5wYXRoKCIvaG9tZS95YW54aXRpbmcvRG9jdW1lbnRzL1Jlc2VhcmNoL0dSQURTX1NBUkNfUEJNQy9zY1JOQV9jb21wYXJlIixwYXN0ZSgiZmVhdHVyZXBsb3RfYmxvb21fNGJfIix0ZW1wLm5hbWVzW2ldLCIucGRmIikpCmdnc2F2ZShvdXRwdXQuZmlsZXBhdGgsZyxkZXZpY2U9InBkZiIsd2lkdGg9MTAsaGVpZ2h0PTUpCn0KCmBgYAoKCiMjIyBJbnRlZ3JhdGVkIEFuYWx5c2lzCkNvbmR1Y3QgaW50ZWdyYXRlZCBhbmFseXNpcyB0byByZW1vdmUgZG9ub3IgZWZmZWN0LiBXZSBoYXZlIG5vdCBzdWNjZWVkZWQgaW4gdGhlIGludGVncmF0aW9uIGFuYWx5c2lzIGR1ZSB0byB0aGUgdmVyeSBzbWFsbCBudW1iZXIgb2YgY2VsbHMgcGVyIHN1YmplY3QuCmBgYHtyIGZpZy53aWR0aD04LGZpZy5oZWlnaHQ9OCxtZXNzYWdlPUZBTFNFfQojIHNwbGl0IHRoZSBkYXRhc2V0IGludG8gYSBsaXN0IG9mIHR3byBzZXVyYXQgb2JqZWN0cyAoc3RpbSBhbmQgQ1RSTCkKaWZuYi5saXN0IDwtIFNwbGl0T2JqZWN0KG15LmRhdGEsIHNwbGl0LmJ5ID0gImRvbm9yIikKdGVtcC5zaXplPC1tYXRyaXgodW5saXN0KGxhcHBseShpZm5iLmxpc3QsZGltKSksbmNvbD0yLGJ5cm93PVQpWywyXQojaWZuYi5saXN0PC1pZm5iLmxpc3RbdGVtcC5zaXplPj0zMF0KCiMgbm9ybWFsaXplIGFuZCBpZGVudGlmeSB2YXJpYWJsZSBmZWF0dXJlcyBmb3IgZWFjaCBkYXRhc2V0IGluZGVwZW5kZW50bHkKaWZuYi5saXN0IDwtIGxhcHBseShYID0gaWZuYi5saXN0LCBGVU4gPSBmdW5jdGlvbih4KSB7CiAgICB4IDwtIE5vcm1hbGl6ZURhdGEoeCkKICAgIHggPC0gRmluZFZhcmlhYmxlRmVhdHVyZXMoeCwgc2VsZWN0aW9uLm1ldGhvZCA9ICJ2c3QiLCBuZmVhdHVyZXMgPSAyMDAwKQp9KQoKIyBzZWxlY3QgZmVhdHVyZXMgdGhhdCBhcmUgcmVwZWF0ZWRseSB2YXJpYWJsZSBhY3Jvc3MgZGF0YXNldHMgZm9yIGludGVncmF0aW9uCmZlYXR1cmVzIDwtIFNlbGVjdEludGVncmF0aW9uRmVhdHVyZXMob2JqZWN0Lmxpc3QgPSBpZm5iLmxpc3QpCmlmbmIubGlzdCA8LSBsYXBwbHkoWCA9IGlmbmIubGlzdCwgRlVOID0gZnVuY3Rpb24oeCkgewogICAgeCA8LSBTY2FsZURhdGEoeCwgZmVhdHVyZXMgPSBmZWF0dXJlcywgdmVyYm9zZSA9IEZBTFNFKQogICAgeCA8LSBSdW5QQ0EoeCwgZmVhdHVyZXMgPSBmZWF0dXJlcywgdmVyYm9zZSA9IEZBTFNFLCBhcHByb3g9RkFMU0UpCn0pCgppbW11bmUuYW5jaG9ycyA8LSBGaW5kSW50ZWdyYXRpb25BbmNob3JzKG9iamVjdC5saXN0ID0gaWZuYi5saXN0LCBhbmNob3IuZmVhdHVyZXMgPSBmZWF0dXJlcywgcmVkdWN0aW9uID0gInJwY2EiKQppbW11bmUuY29tYmluZWQgPC0gSW50ZWdyYXRlRGF0YShhbmNob3JzZXQgPSBpbW11bmUuYW5jaG9ycykKCgpteS5hbmNob3JzIDwtIEZpbmRJbnRlZ3JhdGlvbkFuY2hvcnMob2JqZWN0Lmxpc3QgPSBpZm5iLmxpc3QsIGFuY2hvci5mZWF0dXJlcyA9IGZlYXR1cmVzKQpteS5jb21iaW5lZCA8LSBJbnRlZ3JhdGVEYXRhKGFuY2hvcnNldCA9IG15LmFuY2hvcnMpCgpEZWZhdWx0QXNzYXkobXkuY29tYmluZWQpIDwtICJpbnRlZ3JhdGVkIgoKIyBSdW4gdGhlIHN0YW5kYXJkIHdvcmtmbG93IGZvciB2aXN1YWxpemF0aW9uIGFuZCBjbHVzdGVyaW5nCm15LmNvbWJpbmVkIDwtIFNjYWxlRGF0YShteS5jb21iaW5lZCwgdmVyYm9zZSA9IEZBTFNFKQpteS5jb21iaW5lZCA8LSBSdW5QQ0EobXkuY29tYmluZWQsIG5wY3MgPSAzMCwgdmVyYm9zZSA9IEZBTFNFKQpteS5jb21iaW5lZCA8LSBSdW5VTUFQKG15LmNvbWJpbmVkLCByZWR1Y3Rpb24gPSAicGNhIiwgZGltcyA9IDE6MzApCm15LmNvbWJpbmVkIDwtIEZpbmROZWlnaGJvcnMobXkuY29tYmluZWQsIHJlZHVjdGlvbiA9ICJwY2EiLCBkaW1zID0gMTozMCkKbXkuY29tYmluZWQgPC0gRmluZENsdXN0ZXJzKG15LmNvbWJpbmVkLCByZXNvbHV0aW9uID0gMC41KQpwMSA8LSBEaW1QbG90KG15LmNvbWJpbmVkLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gImRvbm9yIikKcDIgPC0gRGltUGxvdChteS5jb21iaW5lZCwgcmVkdWN0aW9uID0gInVtYXAiLCBsYWJlbCA9IFRSVUUsIHJlcGVsID0gVFJVRSxzaHVmZmxlPVQpCmBgYAoKCgpgYGB7cn0KIyBsZWZ0IG92ZXIKbXkuZGF0YUBhc3NheXMkaW50ZWdyYXRlZDwtc2MuZGF0YUBhc3NheXMkaW50ZWdyYXRlZApteS5kYXRhQG1ldGEuZGF0YTwtY2JpbmQobXkuZGF0YUBtZXRhLmRhdGEsbXljb2wuZGF0YSkKCklkZW50cyhteS5kYXRhKTwtbXkuZGF0YUBtZXRhLmRhdGEkY2VsbHR5cGUKIyBpZGVudGlmeSB0aGUgY2VsbCB0eXBlIG1hcmtlciBnZW5lcwojSWRlbnRzKHNjLmRhdGEpPC1zYy5kYXRhQG1ldGEuZGF0YSRjZWxsdHlwZQpjZWxsdHlwZS5uYW1lczwtdW5pcXVlKGFzLmNoYXJhY3RlcihteS5kYXRhQG1ldGEuZGF0YSRjZWxsdHlwZSkpCnNjLm1hcmtlcnMucmVzdWx0PC1saXN0KCkKCmZvcihpIGluIDE6bGVuZ3RoKGNlbGx0eXBlLm5hbWVzKSl7CiAgc2MubWFya2Vycy5yZXN1bHRbW2ldXTwtRmluZE1hcmtlcnMobXkuZGF0YSxpZGVudC4xPWNlbGx0eXBlLm5hbWVzW2ldLG1pbi5wY3Q9MC4wNSkKfQoKIyBzYXZlIHRoZSBtYXJrZXIgbGlzdHMKI291dHB1dC5maWxlcGF0aDwtZmlsZS5wYXRoKCJ+L2RyaXZlcl9HcmFjZS9zY3JhdGNoL0dSQURTL1NBUkNfcmVzdWx0cy9SZXN1bHRzX3N1bW1hcnlfUEJNQ19oZzM4L2Jhc2VsaW5lL2RhdGEvR1NFMTMyMzM4X1NpbmdsZUNlbGxFeHBlcmltZW50X21hcmtlcmxpc3QuUkRTIikKb3V0cHV0LmZpbGVwYXRoPC0iL2hvbWUveWFueGl0aW5nL0RvY3VtZW50cy9SZXNlYXJjaC9HUkFEU19TQVJDX1BCTUMvc2NSTkFfY29tcGFyZS9HU0UxMzIzMzhfU2luZ2xlQ2VsbEV4cGVyaW1lbnRfbWFya2VybGlzdC5SRFMiCnNhdmVSRFMoc2MubWFya2Vycy5yZXN1bHQsZmlsZT1vdXRwdXQuZmlsZXBhdGgscmVmaG9vayA9IE5VTEwpCmBgYAoKRHJhdyB0aGUgVU1BUHMgdG8gc2hvdyB0aGUgZXhwcmVzc2lvbiBvZiBibG9vbSBnZW5lcyBpbiB0aGlzIDEwWCBkYXRhLgpgYGB7ciBmaWcud2lkdGg9OCxmaWcuaGVpZ2h0PTh9CiNibG9vbS5maWxlcGF0aDwtIn4vZHJpdmVyX0dyYWNlL3NjcmF0Y2gvR1JBRFMvU0FSQ19yZXN1bHRzL1Jlc3VsdHNfc3VtbWFyeV9QQk1DX2hnMzgvYmFzZWxpbmUvc2NSTkFfY29tcGFyZS9hbmFseXNpczRiX0JMT09NLnRzdiIKbXkuZGlzdGluY3QuY29sb3JzPC1jKCcjZTYxOTRiJyAsICcjM2NiNDRiJywgJyNmZmUxMTknLCAnIzQzNjNkOCcsICcjZjU4MjMxJywgJyM5MTFlYjQnLCAnIzQ2ZjBmMCcsICcjZjAzMmU2JywgJyNiY2Y2MGMnLCAnI2ZhYmViZScsICcjMDA4MDgwJywgJyNlNmJlZmYnLCAnIzlhNjMyNCcsICcjODA4MDgwJywgJyM4MDAwMDAnLCAgJyM4MDgwMDAnLCAnI2ZmZDhiMScsICcjMDAwMDc1JywgICcjZmZmZmZmJywgJyMwMDAwMDAnLCcjZmZmYWM4JywnI2FhZmZjMycpCmNlbGx0eXBlLmNvbG9yczwtbXkuZGlzdGluY3QuY29sb3JzWzE6bGVuZ3RoKHVuaXF1ZShteS5kYXRhQG1ldGEuZGF0YSRjZWxsdHlwZSkpXQpibG9vbS5maWxlcGF0aDwtIi9ob21lL3lhbnhpdGluZy9Eb2N1bWVudHMvUmVzZWFyY2gvR1JBRFNfU0FSQ19QQk1DL0RhdGEvYW5hbHlzaXM0Yl9CTE9PTS50c3YiCmJsb29tLmRhdGE8LWFzLmRhdGEuZnJhbWUocmVhZF90c3YoYmxvb20uZmlsZXBhdGgpKQpibG9vbS5nZW5lczwtYmxvb20uZGF0YVtibG9vbS5kYXRhJGBDT1JFIEVOUklDSE1FTlRgPT0iWWVzIiwiU1lNQk9MIl0KCnRlbXAubmFtZXM8LWludGVyc2VjdChibG9vbS5nZW5lcyxyb3duYW1lcyhzYy5kYXRhQGFzc2F5cyRpbnRlZ3JhdGVkQGNvdW50cykpCgojb3V0cHV0LmZpbGVwYXRoPC0ifi9kcml2ZXJfR3JhY2Uvc2NyYXRjaC9HUkFEUy9TQVJDX3Jlc3VsdHMvUmVzdWx0c19zdW1tYXJ5X1BCTUNfaGczOC9iYXNlbGluZS9zY1JOQV9jb21wYXJlL2ZlYXR1cmVwbG90X2Jsb29tXzRiLnBkZiIKb3V0cHV0LmZpbGVwYXRoPC0iL2hvbWUveWFueGl0aW5nL0RvY3VtZW50cy9SZXNlYXJjaC9HUkFEU19TQVJDX1BCTUMvc2NSTkFfY29tcGFyZS9mZWF0dXJlcGxvdF9ibG9vbV80Yi5wZGYiCnBkZihvdXRwdXQuZmlsZXBhdGgsd2lkdGggPSA4LGhlaWdodD04LG9uZWZpbGUgPSBUKQojZy5saXN0PC1saXN0KCkKCmZvcihpIGluIDE6bGVuZ3RoKHRlbXAubmFtZXMpKXsKdGVtcC5kYXRhPC1teS5kYXRhQG1ldGEuZGF0YQpnLjE8LWdncGxvdCh0ZW1wLmRhdGEsYWVzKHg9VU1BUF8xLHk9VU1BUF8yLGNvbG91cj1jZWxsdHlwZSkpK2dlb21fcG9pbnQoc2l6ZT0wLjEsc2hhcGU9MTkpK3NjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9Y2VsbHR5cGUuY29sb3JzKStndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChzaXplID0gOCkpKQoKdGVtcC5jb2xvcjwtc2MuZGF0YUBhc3NheXMkaW50ZWdyYXRlZEBjb3VudHNbdGVtcC5uYW1lc1tpXSxdCnRlbXAuZGF0YTwtY2JpbmQoc2MuZGF0YUBtZXRhLmRhdGEsdGVtcC5jb2xvcikKY29sbmFtZXModGVtcC5kYXRhKVtuY29sKHRlbXAuZGF0YSldPC0iY29sIgpnLjI8LWdncGxvdCh0ZW1wLmRhdGEsYWVzKHg9VU1BUF8xLHk9VU1BUF8yLGNvbG91cj1jb2wpKStnZW9tX3BvaW50KHNpemU9MC4wMSxzaGFwZT0xOSkrc2NhbGVfY29sb3JfZ3JhZGllbnQobG93PSJncmV5IixoaWdoPSJyZWQiKStnZ3RpdGxlKHRlbXAubmFtZXNbaV0pCgojb3V0cHV0LmZpbGVwYXRoPC1maWxlLnBhdGgoIn4vZHJpdmVyX0dyYWNlL3NjcmF0Y2gvR1JBRFMvU0FSQ19yZXN1bHRzL1Jlc3VsdHNfc3VtbWFyeV9QQk1DX2hnMzgvYmFzZWxpbmUvc2NSTkFfY29tcGFyZSIscGFzdGUoImZlYXR1cmVwbG90X2Jsb29tXzRiXyIsdGVtcC5uYW1lc1tpXSwiLnBkZiIpKQpnPC1ncmlkLmFycmFuZ2UoZy4xLGcuMixuY29sPTIpCmcKb3V0cHV0LmZpbGVwYXRoPC1maWxlLnBhdGgoIi9ob21lL3lhbnhpdGluZy9Eb2N1bWVudHMvUmVzZWFyY2gvR1JBRFNfU0FSQ19QQk1DL3NjUk5BX2NvbXBhcmUiLHBhc3RlKCJmZWF0dXJlcGxvdF9ibG9vbV80Yl8iLHRlbXAubmFtZXNbaV0sIi5wZGYiKSkKZ2dzYXZlKG91dHB1dC5maWxlcGF0aCxnLGRldmljZT0icGRmIix3aWR0aD04LGhlaWdodD04KQoKfQpkZXYub2ZmKCkKCgpgYGAKCkZpcnN0LCB3ZSBldmFsdWF0ZSB0aGUgZW5yaWNobWVudCBvZiBCTE9PTSBnZW5lcyBpbiB0aGUgbWFya2VyIGdlbmUgbGlzdHMK